1 /* 2 * Copyright (c) 2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Tomohiro Kusumi <tkusumi@netbsd.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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 16 * distribution. 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. 20 * 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 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/malloc.h> 37 #include <sys/mutex2.h> 38 #include <sys/objcache.h> 39 #include <sys/callout.h> 40 41 #include <dev/disk/dm/dm.h> 42 43 MALLOC_DEFINE(M_DMDELAY, "dm_delay", "Device Mapper Target Delay"); 44 45 struct dm_delay_buf { 46 TAILQ_ENTRY(dm_delay_buf) entry; 47 struct buf *bp; 48 int expire; 49 }; 50 TAILQ_HEAD(dm_delay_buf_list, dm_delay_buf); 51 52 struct dm_delay_info { 53 dm_pdev_t *pdev; 54 uint64_t offset; 55 int delay; 56 int count; 57 int enabled; 58 struct dm_delay_buf_list buf_list; 59 struct callout cal; 60 struct mtx buf_mtx; 61 struct mtx cal_mtx; 62 struct lwkt_token token; 63 thread_t td; 64 }; 65 66 typedef struct target_delay_config { 67 struct dm_delay_info read; 68 struct dm_delay_info write; 69 int argc; /* either 3 or 6 */ 70 } dm_target_delay_config_t; 71 72 static int _init(struct dm_delay_info*, char**, int); 73 static int _table(struct dm_delay_info*, char*); 74 static void _strategy(struct dm_delay_info*, struct buf*); 75 static __inline void _submit(struct dm_delay_info*, struct buf*); 76 static void _submit_queue(struct dm_delay_info*, int); 77 static void _destroy(struct dm_delay_info*); 78 static void _timeout(void*); 79 static void _thread(void*); 80 static __inline void _debug(struct dm_delay_info*, const char*); 81 82 static struct objcache *obj_cache = NULL; 83 static struct objcache_malloc_args obj_args = { 84 sizeof(struct dm_delay_buf), M_DMDELAY, 85 }; 86 87 static int 88 dm_target_delay_init(dm_table_entry_t *table_en, int argc, char **argv) 89 { 90 dm_target_delay_config_t *tdc; 91 int ret; 92 93 dmdebug("Delay target init: argc=%d\n", argc); 94 if (argc != 3 && argc != 6) { 95 kprintf("Delay target takes 3 or 6 args\n"); 96 return EINVAL; 97 } 98 99 tdc = kmalloc(sizeof(*tdc), M_DMDELAY, M_WAITOK | M_ZERO); 100 if (tdc == NULL) 101 return ENOMEM; 102 tdc->argc = argc; 103 104 ret = _init(&tdc->read, argv, 0); 105 if (ret) { 106 kfree(tdc, M_DMDELAY); 107 return ret; 108 } 109 110 if (argc == 6) 111 argv += 3; 112 113 ret = _init(&tdc->write, argv, 1); 114 if (ret) { 115 dm_pdev_decr(tdc->read.pdev); 116 kfree(tdc, M_DMDELAY); 117 return ret; 118 } 119 120 dm_table_add_deps(table_en, tdc->read.pdev); 121 dm_table_add_deps(table_en, tdc->write.pdev); 122 123 dm_table_init_target(table_en, tdc); 124 125 return 0; 126 } 127 128 static int 129 _init(struct dm_delay_info *di, char **argv, int id) 130 { 131 dm_pdev_t *dmp; 132 int tmp; 133 134 if (argv[0] == NULL) 135 return EINVAL; 136 if ((dmp = dm_pdev_insert(argv[0])) == NULL) 137 return ENOENT; 138 139 di->pdev = dmp; 140 di->offset = atoi64(argv[1]); 141 tmp = atoi64(argv[2]); 142 di->delay = tmp * hz / 1000; 143 di->count = 0; 144 145 TAILQ_INIT(&di->buf_list); 146 callout_init(&di->cal); 147 mtx_init(&di->buf_mtx, "dmdlbuf"); 148 mtx_init(&di->cal_mtx, "dmdlcal"); 149 lwkt_token_init(&di->token, "dmdlthr"); 150 151 di->enabled = 1; 152 lwkt_create(_thread, di, &di->td, NULL, 0, -1, "dmdl%d", id); 153 154 _debug(di, "init"); 155 return 0; 156 } 157 158 static char * 159 dm_target_delay_info(void *target_config) 160 { 161 dm_target_delay_config_t *tdc; 162 char *params; 163 164 tdc = target_config; 165 KKASSERT(tdc != NULL); 166 167 params = dm_alloc_string(DM_MAX_PARAMS_SIZE); 168 ksnprintf(params, DM_MAX_PARAMS_SIZE, 169 "%d %d", tdc->read.count, tdc->write.count); 170 171 return params; 172 } 173 174 static char * 175 dm_target_delay_table(void *target_config) 176 { 177 dm_target_delay_config_t *tdc; 178 char *params, *p; 179 180 tdc = target_config; 181 KKASSERT(tdc != NULL); 182 183 params = dm_alloc_string(DM_MAX_PARAMS_SIZE); 184 p = params; 185 p += _table(&tdc->read, p); 186 if (tdc->argc == 6) { 187 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, " "); 188 _table(&tdc->write, p); 189 } 190 191 return params; 192 } 193 194 static int _table(struct dm_delay_info *di, char *p) 195 { 196 int ret; 197 198 ret = ksnprintf(p, DM_MAX_PARAMS_SIZE, 199 "%s %" PRIu64 " %d", 200 di->pdev->udev_name, di->offset, di->delay); 201 return ret; 202 } 203 204 static int 205 dm_target_delay_strategy(dm_table_entry_t *table_en, struct buf *bp) 206 { 207 dm_target_delay_config_t *tdc; 208 struct dm_delay_info *di; 209 210 tdc = table_en->target_config; 211 KKASSERT(tdc != NULL); 212 213 switch (bp->b_cmd) { 214 case BUF_CMD_READ: 215 di = &tdc->read; 216 break; 217 case BUF_CMD_WRITE: 218 case BUF_CMD_FLUSH: 219 di = &tdc->write; 220 break; 221 default: 222 di = NULL; 223 break; 224 } 225 226 if (di) { 227 if (di->delay) { 228 _strategy(di, bp); 229 } else { 230 _submit(di, bp); 231 } 232 } else { 233 /* XXX */ 234 struct vnode *vnode = tdc->write.pdev->pdev_vnode; 235 vn_strategy(vnode, &bp->b_bio1); 236 } 237 return 0; 238 } 239 240 static void 241 _strategy(struct dm_delay_info *di, struct buf *bp) 242 { 243 struct dm_delay_buf *dp; 244 245 dp = objcache_get(obj_cache, M_WAITOK); 246 dp->bp = bp; 247 dp->expire = ticks + di->delay; 248 249 mtx_lock(&di->buf_mtx); 250 di->count++; 251 TAILQ_INSERT_TAIL(&di->buf_list, dp, entry); 252 mtx_unlock(&di->buf_mtx); 253 254 mtx_lock(&di->cal_mtx); 255 if (!callout_pending(&di->cal)) 256 callout_reset(&di->cal, di->delay, _timeout, di); 257 mtx_unlock(&di->cal_mtx); 258 } 259 260 static __inline 261 void 262 _submit(struct dm_delay_info *di, struct buf *bp) 263 { 264 _debug(di, "submit"); 265 266 bp->b_bio1.bio_offset += di->offset * DEV_BSIZE; 267 vn_strategy(di->pdev->pdev_vnode, &bp->b_bio1); 268 } 269 270 static void 271 _submit_queue(struct dm_delay_info *di, int submit_all) 272 { 273 struct dm_delay_buf *dp; 274 struct dm_delay_buf_list tmp_list; 275 int next = -1; 276 int reset = 0; 277 278 _debug(di, "submitq"); 279 TAILQ_INIT(&tmp_list); 280 281 mtx_lock(&di->buf_mtx); 282 while ((dp = TAILQ_FIRST(&di->buf_list)) != NULL) { 283 if (submit_all || ticks > dp->expire) { 284 TAILQ_REMOVE(&di->buf_list, dp, entry); 285 TAILQ_INSERT_TAIL(&tmp_list, dp, entry); 286 di->count--; 287 continue; 288 } 289 if (reset == 0) { 290 reset = 1; 291 next = dp->expire; 292 } else { 293 next = min(next, dp->expire); 294 } 295 } 296 mtx_unlock(&di->buf_mtx); 297 298 if (reset) { 299 mtx_lock(&di->cal_mtx); 300 callout_reset(&di->cal, next - ticks, _timeout, di); 301 mtx_unlock(&di->cal_mtx); 302 } 303 304 while ((dp = TAILQ_FIRST(&tmp_list)) != NULL) { 305 TAILQ_REMOVE(&tmp_list, dp, entry); 306 _submit(di, dp->bp); 307 objcache_put(obj_cache, dp); 308 } 309 } 310 311 static int 312 dm_target_delay_destroy(dm_table_entry_t *table_en) 313 { 314 dm_target_delay_config_t *tdc; 315 316 tdc = table_en->target_config; 317 if (tdc == NULL) 318 return 0; 319 320 _destroy(&tdc->read); 321 _destroy(&tdc->write); 322 323 kfree(tdc, M_DMDELAY); 324 325 return 0; 326 } 327 328 static void 329 _destroy(struct dm_delay_info *di) 330 { 331 _debug(di, "destroy"); 332 333 lwkt_gettoken(&di->token); 334 di->enabled = 0; 335 336 mtx_lock(&di->cal_mtx); 337 if (callout_pending(&di->cal)) 338 callout_cancel(&di->cal); 339 mtx_unlock(&di->cal_mtx); 340 341 _submit_queue(di, 1); 342 wakeup(di); 343 tsleep(&di->enabled, 0, "dmdldestroy", 0); 344 lwkt_reltoken(&di->token); 345 346 mtx_uninit(&di->cal_mtx); 347 mtx_uninit(&di->buf_mtx); 348 349 dm_pdev_decr(di->pdev); 350 } 351 352 static void 353 _timeout(void *arg) 354 { 355 struct dm_delay_info *di = arg; 356 357 _debug(di, "timeout"); 358 wakeup(di); 359 } 360 361 static void 362 _thread(void *arg) 363 { 364 struct dm_delay_info *di = arg; 365 366 _debug(di, "thread init"); 367 lwkt_gettoken(&di->token); 368 369 while (di->enabled) { 370 tsleep(di, 0, "dmdlthread", 0); 371 _submit_queue(di, 0); 372 } 373 374 di->td = NULL; 375 wakeup(&di->enabled); 376 377 _debug(di, "thread exit"); 378 lwkt_reltoken(&di->token); 379 lwkt_exit(); 380 } 381 382 static __inline 383 void 384 _debug(struct dm_delay_info *di, const char *msg) 385 { 386 dmdebug("%-8s: %d pdev=%s offset=%ju delay=%d count=%d\n", 387 msg, di->enabled, di->pdev->name, 388 (uintmax_t)di->offset, di->delay, di->count); 389 } 390 391 static void 392 _objcache_create(void) 393 { 394 if (obj_cache == NULL) { 395 obj_cache = objcache_create("dmdlobj", 0, 0, NULL, NULL, NULL, 396 objcache_malloc_alloc, 397 objcache_malloc_free, 398 &obj_args); 399 } 400 KKASSERT(obj_cache); 401 } 402 403 static void 404 _objcache_destroy(void) 405 { 406 if (obj_cache) { 407 objcache_destroy(obj_cache); 408 obj_cache = NULL; 409 } 410 } 411 412 static int 413 dmtd_mod_handler(module_t mod, int type, void *unused) 414 { 415 dm_target_t *dmt = NULL; 416 int err = 0; 417 418 switch(type) { 419 case MOD_LOAD: 420 if ((dmt = dm_target_lookup("delay")) != NULL) { 421 dm_target_unbusy(dmt); 422 return EEXIST; 423 } 424 dmt = dm_target_alloc("delay"); 425 dmt->version[0] = 1; 426 dmt->version[1] = 0; 427 dmt->version[2] = 0; 428 dmt->init = &dm_target_delay_init; 429 dmt->destroy = &dm_target_delay_destroy; 430 dmt->strategy = &dm_target_delay_strategy; 431 dmt->table = &dm_target_delay_table; 432 dmt->info = &dm_target_delay_info; 433 434 _objcache_create(); 435 err = dm_target_insert(dmt); 436 if (err == 0) 437 kprintf("dm_target_delay: Successfully initialized\n"); 438 break; 439 440 case MOD_UNLOAD: 441 err = dm_target_remove("delay"); 442 if (err == 0) 443 kprintf("dm_target_delay: unloaded\n"); 444 _objcache_destroy(); 445 break; 446 } 447 448 return err; 449 } 450 451 DM_TARGET_MODULE(dm_target_delay, dmtd_mod_handler); 452