Home | History | Annotate | Line # | Download | only in dm
      1 /*        $NetBSD: dm_target_delay.c,v 1.2 2020/01/21 16:27:53 tkusumi Exp $      */
      2 
      3 /*
      4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
      5  * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
      6  * All rights reserved.
      7  *
      8  * This code is derived from software contributed to The NetBSD Foundation
      9  * by Tomohiro Kusumi <tkusumi (at) netbsd.org>.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: dm_target_delay.c,v 1.2 2020/01/21 16:27:53 tkusumi Exp $");
     34 
     35 #include <sys/types.h>
     36 #include <sys/param.h>
     37 #include <sys/kernel.h>
     38 #include <sys/buf.h>
     39 #include <sys/kmem.h>
     40 #include <sys/mutex.h>
     41 #include <sys/queue.h>
     42 #include <sys/callout.h>
     43 #include <sys/pool.h>
     44 #include <sys/kthread.h>
     45 
     46 #include "dm.h"
     47 
     48 struct dm_delay_buf {
     49 	TAILQ_ENTRY(dm_delay_buf) entry;
     50 	struct buf *bp;
     51 	int expire;
     52 };
     53 TAILQ_HEAD(dm_delay_buf_list, dm_delay_buf);
     54 
     55 struct dm_delay_info {
     56 	dm_pdev_t *pdev;
     57 	uint64_t offset;
     58 	int delay;
     59 	int count;
     60 	int enabled;
     61 	struct dm_delay_buf_list buf_list;
     62 	struct callout cal;
     63 	kmutex_t buf_mtx;
     64 	kmutex_t cal_mtx;
     65 	kmutex_t token; /* lwkt_token in DragonFly */
     66 };
     67 
     68 typedef struct target_delay_config {
     69 	struct dm_delay_info read;
     70 	struct dm_delay_info write;
     71 	int argc; /* either 3 or 6 */
     72 } dm_target_delay_config_t;
     73 
     74 static int _init(struct dm_delay_info*, char**);
     75 static int _table(struct dm_delay_info*, char*);
     76 static void _strategy(struct dm_delay_info*, struct buf*);
     77 static __inline void _submit(struct dm_delay_info*, struct buf*);
     78 static void _destroy(struct dm_delay_info*);
     79 static void _timeout(void*);
     80 static void _thread(void*);
     81 static __inline void _debug(struct dm_delay_info*, const char*);
     82 
     83 static struct pool pool;
     84 static bool pool_initialized = false;
     85 
     86 #ifdef DM_TARGET_MODULE
     87 /*
     88  * Every target can be compiled directly to dm driver or as a
     89  * separate module this part of target is used for loading targets
     90  * to dm driver.
     91  * Target can be unloaded from kernel only if there are no users of
     92  * it e.g. there are no devices which uses that target.
     93  */
     94 #include <sys/kernel.h>
     95 #include <sys/module.h>
     96 
     97 MODULE(MODULE_CLASS_MISC, dm_target_delay, NULL);
     98 
     99 static int
    100 dm_target_delay_modcmd(modcmd_t cmd, void *arg)
    101 {
    102 	dm_target_t *dmt;
    103 	int r;
    104 
    105 	switch (cmd) {
    106 	case MODULE_CMD_INIT:
    107 		if ((dmt = dm_target_lookup("delay")) != NULL) {
    108 			dm_target_unbusy(dmt);
    109 			return EEXIST;
    110 		}
    111 		dmt = dm_target_alloc("delay");
    112 
    113 		dmt->version[0] = 1;
    114 		dmt->version[1] = 0;
    115 		dmt->version[2] = 0;
    116 		dmt->init = &dm_target_delay_init;
    117 		dmt->table = &dm_target_delay_table;
    118 		dmt->strategy = &dm_target_delay_strategy;
    119 		dmt->sync = &dm_target_delay_sync;
    120 		dmt->destroy = &dm_target_delay_destroy;
    121 		//dmt->upcall = &dm_target_delay_upcall;
    122 		dmt->secsize = &dm_target_delay_secsize;
    123 
    124 		dm_target_delay_pool_create();
    125 		r = dm_target_insert(dmt);
    126 
    127 		break;
    128 
    129 	case MODULE_CMD_FINI:
    130 		r = dm_target_rem("delay");
    131 		dm_target_delay_pool_destroy();
    132 		break;
    133 
    134 	case MODULE_CMD_STAT:
    135 		return ENOTTY;
    136 
    137 	default:
    138 		return ENOTTY;
    139 	}
    140 
    141 	return r;
    142 }
    143 #endif
    144 
    145 int
    146 dm_target_delay_init(dm_table_entry_t *table_en, int argc, char **argv)
    147 {
    148 	dm_target_delay_config_t *tdc;
    149 	int ret;
    150 
    151 	if (argc != 3 && argc != 6) {
    152 		printf("Delay target takes 3 or 6 args, %d given\n", argc);
    153 		return EINVAL;
    154 	}
    155 
    156 	aprint_debug("Delay target init function called: argc=%d\n", argc);
    157 
    158 	tdc = kmem_alloc(sizeof(dm_target_delay_config_t), KM_SLEEP);
    159 	tdc->argc = argc;
    160 
    161 	ret = _init(&tdc->read, argv);
    162 	if (ret) {
    163 		kmem_free(tdc, sizeof(*tdc));
    164 		return ret;
    165 	}
    166 
    167 	if (argc == 6)
    168 		argv += 3;
    169 
    170 	ret = _init(&tdc->write, argv);
    171 	if (ret) {
    172 		dm_pdev_decr(tdc->read.pdev);
    173 		kmem_free(tdc, sizeof(*tdc));
    174 		return ret;
    175 	}
    176 
    177 	dm_table_add_deps(table_en, tdc->read.pdev);
    178 	dm_table_add_deps(table_en, tdc->write.pdev);
    179 
    180 	table_en->target_config = tdc;
    181 
    182 	return 0;
    183 }
    184 
    185 static int
    186 _init(struct dm_delay_info *di, char **argv)
    187 {
    188 	dm_pdev_t *dmp;
    189 	int tmp;
    190 
    191 	if (argv[0] == NULL)
    192 		return EINVAL;
    193 	if ((dmp = dm_pdev_insert(argv[0])) == NULL)
    194 		return ENOENT;
    195 
    196 	di->pdev = dmp;
    197 	di->offset = atoi64(argv[1]);
    198 	tmp = atoi64(argv[2]);
    199 	di->delay = tmp * hz / 1000;
    200 	di->count = 0;
    201 
    202 	TAILQ_INIT(&di->buf_list);
    203 	callout_init(&di->cal, 0);
    204 	mutex_init(&di->buf_mtx, MUTEX_DEFAULT, IPL_NONE);
    205 	mutex_init(&di->cal_mtx, MUTEX_DEFAULT, IPL_NONE);
    206 	mutex_init(&di->token, MUTEX_DEFAULT, IPL_NONE);
    207 
    208 	di->enabled = 1;
    209 	if (kthread_create(PRI_NONE, 0, NULL, _thread, di, NULL,
    210 	    "dmdlthread")) {
    211 		printf("Failed to create delay target's kthread\n");
    212 		/* XXX cleanup ? */
    213 		return EINVAL;
    214 	}
    215 
    216 	_debug(di, "init");
    217 	return 0;
    218 }
    219 
    220 char *
    221 dm_target_delay_info(void *target_config)
    222 {
    223 	dm_target_delay_config_t *tdc;
    224 	char *params;
    225 
    226 	tdc = target_config;
    227 	KASSERT(tdc != NULL);
    228 
    229 	aprint_debug("Delay target info function called\n");
    230 
    231 	params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
    232 	snprintf(params, DM_MAX_PARAMS_SIZE, "%d %d", tdc->read.count,
    233 	    tdc->write.count);
    234 
    235 	return params;
    236 }
    237 
    238 char *
    239 dm_target_delay_table(void *target_config)
    240 {
    241 	dm_target_delay_config_t *tdc;
    242 	char *params, *p;
    243 
    244 	tdc = target_config;
    245 	KASSERT(tdc != NULL);
    246 
    247 	aprint_debug("Delay target table function called\n");
    248 
    249 	params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
    250 	p = params;
    251 	p += _table(&tdc->read, p);
    252 	if (tdc->argc == 6) {
    253 		p += snprintf(p, DM_MAX_PARAMS_SIZE, " ");
    254 		_table(&tdc->write, p);
    255 	}
    256 
    257 	return params;
    258 }
    259 
    260 static int
    261 _table(struct dm_delay_info *di, char *p)
    262 {
    263 
    264 	return snprintf(p, DM_MAX_PARAMS_SIZE, "%s %" PRIu64 " %d",
    265 	    di->pdev->udev_name, di->offset, di->delay);
    266 }
    267 
    268 int
    269 dm_target_delay_strategy(dm_table_entry_t *table_en, struct buf *bp)
    270 {
    271 	dm_target_delay_config_t *tdc;
    272 	struct dm_delay_info *di;
    273 
    274 	tdc = table_en->target_config;
    275 	KASSERT(tdc != NULL);
    276 
    277 	if (bp->b_flags & B_READ)
    278 		di = &tdc->read;
    279 	else
    280 		di = &tdc->write;
    281 
    282 	if (di) {
    283 		if (di->delay) {
    284 			_strategy(di, bp);
    285 		} else {
    286 			_submit(di, bp);
    287 		}
    288 	} else {
    289 		struct vnode *vnode = tdc->write.pdev->pdev_vnode;
    290 		KASSERT(0);
    291 		VOP_STRATEGY(vnode, bp);
    292 	}
    293 
    294 	return 0;
    295 }
    296 
    297 static void
    298 _strategy(struct dm_delay_info *di, struct buf *bp)
    299 {
    300 	struct dm_delay_buf *dp;
    301 
    302 	dp = pool_get(&pool, PR_WAITOK);
    303 	dp->bp = bp;
    304 	dp->expire = tick + di->delay;
    305 
    306 	mutex_enter(&di->buf_mtx);
    307 	di->count++;
    308 	TAILQ_INSERT_TAIL(&di->buf_list, dp, entry);
    309 	mutex_exit(&di->buf_mtx);
    310 
    311 	mutex_enter(&di->cal_mtx);
    312 	if (!callout_pending(&di->cal))
    313 		callout_reset(&di->cal, di->delay, _timeout, di);
    314 	mutex_exit(&di->cal_mtx);
    315 }
    316 
    317 static __inline void
    318 _submit(struct dm_delay_info *di, struct buf *bp)
    319 {
    320 
    321 	_debug(di, "submit");
    322 
    323 	bp->b_blkno += di->offset;
    324 	VOP_STRATEGY(di->pdev->pdev_vnode, bp);
    325 }
    326 
    327 static void
    328 _submit_queue(struct dm_delay_info *di, int submit_all)
    329 {
    330 	struct dm_delay_buf *dp;
    331 	struct dm_delay_buf_list tmp_list;
    332 	int next = -1;
    333 	int reset = 0;
    334 
    335 	_debug(di, "submitq");
    336 	TAILQ_INIT(&tmp_list);
    337 
    338 	mutex_enter(&di->buf_mtx);
    339 	while ((dp = TAILQ_FIRST(&di->buf_list)) != NULL) {
    340 		if (submit_all || tick > dp->expire) {
    341 			TAILQ_REMOVE(&di->buf_list, dp, entry);
    342 			TAILQ_INSERT_TAIL(&tmp_list, dp, entry);
    343 			di->count--;
    344 			continue;
    345 		}
    346 		if (reset == 0) {
    347 			reset = 1;
    348 			next = dp->expire;
    349 		} else {
    350 			next = lmin(next, dp->expire);
    351 		}
    352 	}
    353 	mutex_exit(&di->buf_mtx);
    354 
    355 	if (reset) {
    356 		mutex_enter(&di->cal_mtx);
    357 		callout_reset(&di->cal, next - tick, _timeout, di);
    358 		mutex_exit(&di->cal_mtx);
    359 	}
    360 
    361 	while ((dp = TAILQ_FIRST(&tmp_list)) != NULL) {
    362 		TAILQ_REMOVE(&tmp_list, dp, entry);
    363 		_submit(di, dp->bp);
    364 		pool_put(&pool, dp);
    365 	}
    366 }
    367 
    368 int
    369 dm_target_delay_sync(dm_table_entry_t *table_en)
    370 {
    371 	dm_target_delay_config_t *tdc;
    372 	int cmd;
    373 
    374 	tdc = table_en->target_config;
    375 	cmd = 1;
    376 
    377 	return VOP_IOCTL(tdc->write.pdev->pdev_vnode, DIOCCACHESYNC, &cmd,
    378 	    FREAD | FWRITE, kauth_cred_get());
    379 }
    380 
    381 int
    382 dm_target_delay_destroy(dm_table_entry_t *table_en)
    383 {
    384 	dm_target_delay_config_t *tdc;
    385 
    386 	tdc = table_en->target_config;
    387 	if (tdc == NULL)
    388 		goto out;
    389 
    390 	_destroy(&tdc->read);
    391 	_destroy(&tdc->write);
    392 
    393 	kmem_free(tdc, sizeof(*tdc));
    394 out:
    395 	/* Unbusy target so we can unload it */
    396 	dm_target_unbusy(table_en->target);
    397 
    398 	return 0;
    399 }
    400 
    401 static void
    402 _destroy(struct dm_delay_info *di)
    403 {
    404 
    405 	_debug(di, "destroy");
    406 
    407 	mutex_enter(&di->token);
    408 	di->enabled = 0;
    409 
    410 	mutex_enter(&di->cal_mtx);
    411 	if (callout_pending(&di->cal))
    412 		callout_halt(&di->cal, NULL);
    413 	mutex_exit(&di->cal_mtx);
    414 
    415 	_submit_queue(di, 1);
    416 	mutex_exit(&di->token);
    417 	wakeup(di);
    418 	/* XXX race here (DragonFly uses lwkt_token instead of mutex) */
    419 	tsleep(&di->enabled, 0, "dmdldestroy", 0);
    420 
    421 	mutex_destroy(&di->token);
    422 	mutex_destroy(&di->cal_mtx);
    423 	mutex_destroy(&di->buf_mtx);
    424 
    425 	dm_pdev_decr(di->pdev);
    426 }
    427 
    428 #if 0
    429 int
    430 dm_target_delay_upcall(dm_table_entry_t *table_en, struct buf *bp)
    431 {
    432 
    433 	return 0;
    434 }
    435 #endif
    436 
    437 int
    438 dm_target_delay_secsize(dm_table_entry_t *table_en, unsigned int *secsizep)
    439 {
    440 	dm_target_delay_config_t *tdc;
    441 	unsigned int secsize;
    442 
    443 	secsize = 0;
    444 
    445 	tdc = table_en->target_config;
    446 	if (tdc != NULL)
    447 		secsize = tdc->write.pdev->pdev_secsize;
    448 
    449 	*secsizep = secsize;
    450 
    451 	return 0;
    452 }
    453 
    454 static void
    455 _timeout(void *arg)
    456 {
    457 	struct dm_delay_info *di = arg;
    458 
    459 	_debug(di, "timeout");
    460 	wakeup(di);
    461 }
    462 
    463 static void
    464 _thread(void *arg)
    465 {
    466 	struct dm_delay_info *di = arg;
    467 
    468 	_debug(di, "thread init");
    469 	mutex_enter(&di->token);
    470 
    471 	while (di->enabled) {
    472 		mutex_exit(&di->token);
    473 		tsleep(di, 0, "dmdlthread", 0);
    474 		_submit_queue(di, 0);
    475 		mutex_enter(&di->token);
    476 	}
    477 
    478 	mutex_exit(&di->token);
    479 	wakeup(&di->enabled);
    480 
    481 	_debug(di, "thread exit");
    482 	kthread_exit(0);
    483 }
    484 
    485 static __inline void
    486 _debug(struct dm_delay_info *di, const char *msg)
    487 {
    488 
    489 	aprint_debug("%-8s: %d pdev=%s offset=%ju delay=%d count=%d\n",
    490 	    msg, di->enabled, di->pdev->name, (uintmax_t)di->offset, di->delay,
    491 	    di->count);
    492 }
    493 
    494 void
    495 dm_target_delay_pool_create(void)
    496 {
    497 
    498 	if (!pool_initialized) {
    499 		pool_init(&pool, sizeof(struct dm_delay_buf), 0, 0, 0,
    500 		    "dmdlobj", &pool_allocator_nointr, IPL_NONE);
    501 		pool_initialized = true;
    502 		aprint_debug("Delay target pool initialized\n");
    503 	}
    504 }
    505 
    506 void
    507 dm_target_delay_pool_destroy(void)
    508 {
    509 
    510 	if (pool_initialized) {
    511 		pool_destroy(&pool);
    512 		pool_initialized = false;
    513 		aprint_debug("Delay target pool destroyed\n");
    514 	}
    515 }
    516