2 * Copyright (c) 2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/mutex2.h>
36 #include <sys/objcache.h>
37 #include <sys/callout.h>
39 #include <dev/disk/dm/dm.h>
41 MALLOC_DEFINE(M_DMDELAY
, "dm_delay", "Device Mapper Target Delay");
44 TAILQ_ENTRY(dm_delay_buf
) entry
;
48 TAILQ_HEAD(dm_delay_buf_list
, dm_delay_buf
);
50 struct dm_delay_info
{
56 struct dm_delay_buf_list buf_list
;
60 struct lwkt_token token
;
64 typedef struct target_delay_config
{
65 struct dm_delay_info read
;
66 struct dm_delay_info write
;
67 int argc
; /* either 3 or 6 */
68 } dm_target_delay_config_t
;
70 static int _init(struct dm_delay_info
*, char**, int);
71 static int _table(struct dm_delay_info
*, char*);
72 static void _strategy(struct dm_delay_info
*, struct buf
*);
73 static __inline
void _submit(struct dm_delay_info
*, struct buf
*);
74 static void _submit_queue(struct dm_delay_info
*, int);
75 static void _destroy(struct dm_delay_info
*);
76 static void _timeout(void*);
77 static void _thread(void*);
78 static __inline
void _debug(struct dm_delay_info
*, const char*);
80 static struct objcache
*obj_cache
= NULL
;
81 static struct objcache_malloc_args obj_args
= {
82 sizeof(struct dm_delay_buf
), M_DMDELAY
,
86 dm_target_delay_init(dm_table_entry_t
*table_en
, int argc
, char **argv
)
88 dm_target_delay_config_t
*tdc
;
91 dmdebug("Delay target init: argc=%d\n", argc
);
92 if (argc
!= 3 && argc
!= 6) {
93 kprintf("Delay target takes 3 or 6 args\n");
97 tdc
= kmalloc(sizeof(*tdc
), M_DMDELAY
, M_WAITOK
| M_ZERO
);
102 ret
= _init(&tdc
->read
, argv
, 0);
104 kfree(tdc
, M_DMDELAY
);
111 ret
= _init(&tdc
->write
, argv
, 1);
113 dm_pdev_decr(tdc
->read
.pdev
);
114 kfree(tdc
, M_DMDELAY
);
118 dm_table_add_deps(table_en
, tdc
->read
.pdev
);
119 dm_table_add_deps(table_en
, tdc
->write
.pdev
);
121 dm_table_init_target(table_en
, tdc
);
127 _init(struct dm_delay_info
*di
, char **argv
, int id
)
134 if ((dmp
= dm_pdev_insert(argv
[0])) == NULL
)
138 di
->offset
= atoi64(argv
[1]);
139 tmp
= atoi64(argv
[2]);
140 di
->delay
= tmp
* hz
/ 1000;
143 TAILQ_INIT(&di
->buf_list
);
144 callout_init(&di
->cal
);
145 mtx_init(&di
->buf_mtx
, "dmdlbuf");
146 mtx_init(&di
->cal_mtx
, "dmdlcal");
147 lwkt_token_init(&di
->token
, "dmdlthr");
150 lwkt_create(_thread
, di
, &di
->td
, NULL
, 0, -1, "dmdl%d", id
);
157 dm_target_delay_info(void *target_config
)
159 dm_target_delay_config_t
*tdc
;
163 KKASSERT(tdc
!= NULL
);
165 params
= dm_alloc_string(DM_MAX_PARAMS_SIZE
);
166 ksnprintf(params
, DM_MAX_PARAMS_SIZE
,
167 "%d %d", tdc
->read
.count
, tdc
->write
.count
);
173 dm_target_delay_table(void *target_config
)
175 dm_target_delay_config_t
*tdc
;
179 KKASSERT(tdc
!= NULL
);
181 params
= dm_alloc_string(DM_MAX_PARAMS_SIZE
);
183 p
+= _table(&tdc
->read
, p
);
184 if (tdc
->argc
== 6) {
185 p
+= ksnprintf(p
, DM_MAX_PARAMS_SIZE
, " ");
186 _table(&tdc
->write
, p
);
192 static int _table(struct dm_delay_info
*di
, char *p
)
196 ret
= ksnprintf(p
, DM_MAX_PARAMS_SIZE
,
198 di
->pdev
->udev_name
, di
->offset
, di
->delay
);
203 dm_target_delay_strategy(dm_table_entry_t
*table_en
, struct buf
*bp
)
205 dm_target_delay_config_t
*tdc
;
206 struct dm_delay_info
*di
;
208 tdc
= table_en
->target_config
;
209 KKASSERT(tdc
!= NULL
);
232 struct vnode
*vnode
= tdc
->write
.pdev
->pdev_vnode
;
233 vn_strategy(vnode
, &bp
->b_bio1
);
239 _strategy(struct dm_delay_info
*di
, struct buf
*bp
)
241 struct dm_delay_buf
*dp
;
243 dp
= objcache_get(obj_cache
, M_WAITOK
);
245 dp
->expire
= ticks
+ di
->delay
;
247 mtx_lock(&di
->buf_mtx
);
249 TAILQ_INSERT_TAIL(&di
->buf_list
, dp
, entry
);
250 mtx_unlock(&di
->buf_mtx
);
252 mtx_lock(&di
->cal_mtx
);
253 if (!callout_pending(&di
->cal
))
254 callout_reset(&di
->cal
, di
->delay
, _timeout
, di
);
255 mtx_unlock(&di
->cal_mtx
);
260 _submit(struct dm_delay_info
*di
, struct buf
*bp
)
262 _debug(di
, "submit");
264 bp
->b_bio1
.bio_offset
+= di
->offset
* DEV_BSIZE
;
265 vn_strategy(di
->pdev
->pdev_vnode
, &bp
->b_bio1
);
269 _submit_queue(struct dm_delay_info
*di
, int submit_all
)
271 struct dm_delay_buf
*dp
;
272 struct dm_delay_buf_list tmp_list
;
276 _debug(di
, "submitq");
277 TAILQ_INIT(&tmp_list
);
279 mtx_lock(&di
->buf_mtx
);
280 while ((dp
= TAILQ_FIRST(&di
->buf_list
)) != NULL
) {
281 if (submit_all
|| ticks
> dp
->expire
) {
282 TAILQ_REMOVE(&di
->buf_list
, dp
, entry
);
283 TAILQ_INSERT_TAIL(&tmp_list
, dp
, entry
);
291 next
= min(next
, dp
->expire
);
294 mtx_unlock(&di
->buf_mtx
);
297 mtx_lock(&di
->cal_mtx
);
298 callout_reset(&di
->cal
, next
- ticks
, _timeout
, di
);
299 mtx_unlock(&di
->cal_mtx
);
302 while ((dp
= TAILQ_FIRST(&tmp_list
)) != NULL
) {
303 TAILQ_REMOVE(&tmp_list
, dp
, entry
);
305 objcache_put(obj_cache
, dp
);
310 dm_target_delay_destroy(dm_table_entry_t
*table_en
)
312 dm_target_delay_config_t
*tdc
;
314 tdc
= table_en
->target_config
;
318 _destroy(&tdc
->read
);
319 _destroy(&tdc
->write
);
321 kfree(tdc
, M_DMDELAY
);
327 _destroy(struct dm_delay_info
*di
)
329 _debug(di
, "destroy");
331 lwkt_gettoken(&di
->token
);
334 mtx_lock(&di
->cal_mtx
);
335 if (callout_pending(&di
->cal
))
336 callout_cancel(&di
->cal
);
337 mtx_unlock(&di
->cal_mtx
);
339 _submit_queue(di
, 1);
341 tsleep(&di
->enabled
, 0, "dmdldestroy", 0);
342 lwkt_reltoken(&di
->token
);
344 mtx_uninit(&di
->cal_mtx
);
345 mtx_uninit(&di
->buf_mtx
);
347 dm_pdev_decr(di
->pdev
);
353 struct dm_delay_info
*di
= arg
;
355 _debug(di
, "timeout");
362 struct dm_delay_info
*di
= arg
;
364 _debug(di
, "thread init");
365 lwkt_gettoken(&di
->token
);
367 while (di
->enabled
) {
368 tsleep(di
, 0, "dmdlthread", 0);
369 _submit_queue(di
, 0);
373 wakeup(&di
->enabled
);
375 _debug(di
, "thread exit");
376 lwkt_reltoken(&di
->token
);
382 _debug(struct dm_delay_info
*di
, const char *msg
)
384 dmdebug("%-8s: %d pdev=%s offset=%ju delay=%d count=%d\n",
385 msg
, di
->enabled
, di
->pdev
->name
,
386 (uintmax_t)di
->offset
, di
->delay
, di
->count
);
390 _objcache_create(void)
392 if (obj_cache
== NULL
) {
393 obj_cache
= objcache_create("dmdlobj", 0, 0, NULL
, NULL
, NULL
,
394 objcache_malloc_alloc
,
395 objcache_malloc_free
,
402 _objcache_destroy(void)
405 objcache_destroy(obj_cache
);
411 dmtd_mod_handler(module_t mod
, int type
, void *unused
)
413 dm_target_t
*dmt
= NULL
;
418 if ((dmt
= dm_target_lookup("delay")) != NULL
) {
419 dm_target_unbusy(dmt
);
422 dmt
= dm_target_alloc("delay");
426 dmt
->init
= &dm_target_delay_init
;
427 dmt
->destroy
= &dm_target_delay_destroy
;
428 dmt
->strategy
= &dm_target_delay_strategy
;
429 dmt
->table
= &dm_target_delay_table
;
430 dmt
->info
= &dm_target_delay_info
;
433 err
= dm_target_insert(dmt
);
435 kprintf("dm_target_delay: Successfully initialized\n");
439 err
= dm_target_remove("delay");
441 kprintf("dm_target_delay: unloaded\n");
449 DM_TARGET_MODULE(dm_target_delay
, dmtd_mod_handler
);