Home | History | Annotate | Line # | Download | only in flash
flash_io.c revision 1.1
      1 /*	$NetBSD: flash_io.c,v 1.1 2011/06/28 18:14:11 ahoka Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2011 Department of Software Engineering,
      5  *		      University of Szeged, Hungary
      6  * Copyright (c) 2011 Adam Hoka <ahoka (at) NetBSD.org>
      7  * All rights reserved.
      8  *
      9  * This code is derived from software contributed to The NetBSD Foundation
     10  * by the Department of Software Engineering, University of Szeged, Hungary
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 __KERNEL_RCSID(0, "$NetBSD: flash_io.c,v 1.1 2011/06/28 18:14:11 ahoka Exp $");
     36 
     37 #include <sys/param.h>
     38 #include <sys/buf.h>
     39 #include <sys/bufq.h>
     40 #include <sys/kernel.h>
     41 #include <sys/kmem.h>
     42 #include <sys/kthread.h>
     43 #include <sys/mutex.h>
     44 #include <sys/sysctl.h>
     45 
     46 #include <dev/flash/flash.h>
     47 #include <dev/flash/flash_io.h>
     48 
     49 #ifdef FLASH_DEBUG
     50 extern int flashdebug;
     51 #endif
     52 
     53 int flash_cachesync_timeout = 1;
     54 int flash_cachesync_nodenum;
     55 
     56 void flash_io_read(struct flash_io *, struct buf *);
     57 void flash_io_write(struct flash_io *, struct buf *);
     58 void flash_io_done(struct flash_io *, struct buf *, int);
     59 int flash_io_cache_write(struct flash_io *, flash_addr_t, struct buf *);
     60 void flash_io_cache_sync(struct flash_io *);
     61 
     62 static int
     63 flash_timestamp_diff(struct bintime *bt, struct bintime *b2)
     64 {
     65 	struct bintime b1 = *bt;
     66 	struct timeval tv;
     67 
     68 	bintime_sub(&b1, b2);
     69 	bintime2timeval(&b1, &tv);
     70 
     71 	return tvtohz(&tv);
     72 }
     73 
     74 static flash_addr_t
     75 flash_io_getblock(struct flash_io *fio, struct buf *bp)
     76 {
     77 	flash_off_t block, last;
     78 
     79 	/* get block number of first byte */
     80 	block = bp->b_rawblkno * DEV_BSIZE / fio->fio_if->erasesize;
     81 
     82 	/* block of the last bite */
     83 	last = (bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1)
     84 	    / fio->fio_if->erasesize;
     85 
     86 	/* spans trough multiple blocks, needs special handling */
     87 	if (last != block) {
     88 		printf("0x%jx -> 0x%jx\n",
     89 		    bp->b_rawblkno * DEV_BSIZE,
     90 		    bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1);
     91 		panic("TODO: multiple block write. last: %jd, current: %jd",
     92 		    (intmax_t )last, (intmax_t )block);
     93 	}
     94 
     95 	return block;
     96 }
     97 
     98 int
     99 flash_sync_thread_init(struct flash_io *fio, struct flash_interface *flash_if)
    100 {
    101 	int error;
    102 
    103 	FLDPRINTF(("starting flash io thread\n"));
    104 
    105 	fio->fio_if = flash_if;
    106 
    107 	fio->fio_data = kmem_alloc(fio->fio_if->erasesize, KM_SLEEP);
    108 
    109 	mutex_init(&fio->fio_lock, MUTEX_DEFAULT, IPL_NONE);
    110 	cv_init(&fio->fio_cv, "flashcv");
    111 
    112 	error = bufq_alloc(&fio->fio_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
    113 	if (error)
    114 		goto err_bufq;
    115 
    116 	fio->fio_exiting = false;
    117 	fio->fio_write_pending = false;
    118 
    119 	/* arrange to allocate the kthread */
    120 	error = kthread_create(PRI_NONE, KTHREAD_JOINABLE | KTHREAD_MPSAFE,
    121 	    NULL, flash_sync_thread, fio, &fio->fio_thread, "flashio");
    122 
    123 	if (!error)
    124 		return 0;
    125 
    126 	bufq_free(fio->fio_bufq);
    127 err_bufq:
    128 	cv_destroy(&fio->fio_cv);
    129 	mutex_destroy(&fio->fio_lock);
    130 	kmem_free(fio->fio_data, fio->fio_if->erasesize);
    131 
    132 	return error;
    133 }
    134 
    135 void
    136 flash_sync_thread_destroy(struct flash_io *fio)
    137 {
    138 	FLDPRINTF(("stopping flash io thread\n"));
    139 
    140 	mutex_enter(&fio->fio_lock);
    141 
    142 	fio->fio_exiting = true;
    143 	cv_broadcast(&fio->fio_cv);
    144 
    145 	mutex_exit(&fio->fio_lock);
    146 
    147 	kthread_join(fio->fio_thread);
    148 
    149 	kmem_free(fio->fio_data, fio->fio_if->erasesize);
    150 	bufq_free(fio->fio_bufq);
    151 	mutex_destroy(&fio->fio_lock);
    152 	cv_destroy(&fio->fio_cv);
    153 }
    154 
    155 int
    156 flash_io_submit(struct flash_io *fio, struct buf *bp)
    157 {
    158 	FLDPRINTF(("submitting job to flash io thread: %p\n", bp));
    159 
    160 	if (__predict_false(fio->fio_exiting)) {
    161 		flash_io_done(fio, bp, ENODEV);
    162 		return ENODEV;
    163 	}
    164 
    165 	if (BUF_ISREAD(bp)) {
    166 		FLDPRINTF(("we have a read job\n"));
    167 
    168 		mutex_enter(&fio->fio_lock);
    169 		if (fio->fio_write_pending)
    170 			flash_io_cache_sync(fio);
    171 		mutex_exit(&fio->fio_lock);
    172 
    173 		flash_io_read(fio, bp);
    174 	} else {
    175 		FLDPRINTF(("we have a write job\n"));
    176 
    177 		flash_io_write(fio, bp);
    178 	}
    179 	return 0;
    180 }
    181 
    182 int
    183 flash_io_cache_write(struct flash_io *fio, flash_addr_t block, struct buf *bp)
    184 {
    185 	size_t retlen;
    186 	flash_addr_t base, offset;
    187 	int error;
    188 
    189 	KASSERT(mutex_owned(&fio->fio_lock));
    190 	KASSERT(fio->fio_if->erasesize != 0);
    191 
    192 	base = block * fio->fio_if->erasesize;
    193 	offset = bp->b_rawblkno * DEV_BSIZE - base;
    194 
    195 	FLDPRINTF(("io cache write, offset: %jd\n", (intmax_t )offset));
    196 
    197 	if (!fio->fio_write_pending) {
    198 		fio->fio_block = block;
    199 		/*
    200 		 * fill the cache with data from flash,
    201 		 * so we dont have to bother with gaps later
    202 		 */
    203 		FLDPRINTF(("filling buffer from offset %ju\n", (uintmax_t)base));
    204 		error = fio->fio_if->read(fio->fio_dev,
    205 		    base, fio->fio_if->erasesize,
    206 		    &retlen, fio->fio_data);
    207 		FLDPRINTF(("cache filled\n"));
    208 
    209 		if (error)
    210 			return error;
    211 
    212 		fio->fio_write_pending = true;
    213 		/* save creation time for aging */
    214 		binuptime(&fio->fio_creation);
    215 	}
    216 	/* copy data to cache */
    217 	memcpy(fio->fio_data + offset, bp->b_data, bp->b_resid);
    218 	bufq_put(fio->fio_bufq, bp);
    219 
    220 	/* update timestamp */
    221 	binuptime(&fio->fio_last_write);
    222 
    223 	return 0;
    224 }
    225 
    226 void
    227 flash_io_cache_sync(struct flash_io *fio)
    228 {
    229 	struct flash_erase_instruction ei;
    230 	struct buf *bp;
    231 	size_t retlen;
    232 	flash_addr_t base;
    233 	int error;
    234 
    235 	KASSERT(mutex_owned(&fio->fio_lock));
    236 
    237 	if (!fio->fio_write_pending) {
    238 		FLDPRINTF(("trying to sync with an invalid buffer\n"));
    239 		return;
    240 	}
    241 
    242 	base = fio->fio_block * fio->fio_if->erasesize;
    243 
    244 	FLDPRINTF(("eraseing block at 0x%jx\n", (uintmax_t )base));
    245 	ei.ei_addr = base;
    246 	ei.ei_len = fio->fio_if->erasesize;
    247 	ei.ei_callback = NULL;
    248 	error = fio->fio_if->erase(fio->fio_dev, &ei);
    249 
    250 	if (error) {
    251 		aprint_error_dev(fio->fio_dev, "cannot erase flash flash!\n");
    252 		goto out;
    253 	}
    254 
    255 	FLDPRINTF(("writing %zu bytes to 0x%jx\n",
    256 		fio->fio_if->erasesize, (uintmax_t )base));
    257 
    258 	error = fio->fio_if->write(fio->fio_dev,
    259 	    base, fio->fio_if->erasesize, &retlen, fio->fio_data);
    260 
    261 	if (error || retlen != fio->fio_if->erasesize) {
    262 		aprint_error_dev(fio->fio_dev, "can't sync write cache: %d\n", error);
    263 		goto out;
    264 	}
    265 
    266 out:
    267 	while ((bp = bufq_get(fio->fio_bufq)) != NULL)
    268 		flash_io_done(fio, bp, error);
    269 
    270 	fio->fio_block = -1;
    271 	fio->fio_write_pending = false;
    272 }
    273 
    274 void
    275 flash_sync_thread(void * arg)
    276 {
    277 	struct flash_io *fio = arg;
    278 	struct bintime now;
    279 
    280 	mutex_enter(&fio->fio_lock);
    281 
    282 	while (!fio->fio_exiting) {
    283 		cv_timedwait_sig(&fio->fio_cv, &fio->fio_lock, hz / 4);
    284 		if (!fio->fio_write_pending) {
    285 			continue;
    286 		}
    287 		/* see if the cache is older than 3 seconds (safety limit),
    288 		 * or if we havent touched the cache since more than 1 ms
    289 		 */
    290 		binuptime(&now);
    291 		if (flash_timestamp_diff(&now, &fio->fio_last_write) > hz / 5) {
    292 			FLDPRINTF(("syncing write cache after timeout\n"));
    293 			flash_io_cache_sync(fio);
    294 		} else if (flash_timestamp_diff(&now, &fio->fio_creation)
    295 		    > 3 * hz) {
    296 			aprint_error_dev(fio->fio_dev,
    297 			    "syncing write cache after 3 sec timeout!\n");
    298 			flash_io_cache_sync(fio);
    299 		}
    300 	}
    301 
    302 	mutex_exit(&fio->fio_lock);
    303 
    304 	kthread_exit(0);
    305 }
    306 
    307 void
    308 flash_io_read(struct flash_io *fio, struct buf *bp)
    309 {
    310 	size_t retlen;
    311 	flash_addr_t offset;
    312 	int error;
    313 
    314 	FLDPRINTF(("flash io read\n"));
    315 
    316 	offset = bp->b_rawblkno * DEV_BSIZE;
    317 
    318 	error = fio->fio_if->read(fio->fio_dev, offset, bp->b_resid,
    319 	    &retlen, bp->b_data);
    320 
    321 	flash_io_done(fio, bp, error);
    322 }
    323 
    324 void
    325 flash_io_write(struct flash_io *fio, struct buf *bp)
    326 {
    327 	flash_addr_t block;
    328 
    329 	FLDPRINTF(("flash io write\n"));
    330 
    331 	block = flash_io_getblock(fio, bp);
    332 	FLDPRINTF(("write to block %jd\n", (intmax_t )block));
    333 
    334 	mutex_enter(&fio->fio_lock);
    335 
    336 	if (fio->fio_write_pending && fio->fio_block != block) {
    337 		FLDPRINTF(("writing to new block, syncing caches\n"));
    338 		flash_io_cache_sync(fio);
    339 	}
    340 
    341 	flash_io_cache_write(fio, block, bp);
    342 
    343 	mutex_exit(&fio->fio_lock);
    344 }
    345 
    346 void
    347 flash_io_done(struct flash_io *fio, struct buf *bp, int error)
    348 {
    349 	FLDPRINTF(("io done: %p\n", bp));
    350 
    351 	if (error == 0)
    352 		bp->b_resid = 0;
    353 
    354 	bp->b_error = error;
    355 	biodone(bp);
    356 }
    357 
    358 static int
    359 sysctl_flash_verify(SYSCTLFN_ARGS)
    360 {
    361 	int error, t;
    362 	struct sysctlnode node;
    363 
    364 	node = *rnode;
    365 	t = *(int *)rnode->sysctl_data;
    366 	node.sysctl_data = &t;
    367 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    368 	if (error || newp == NULL)
    369 		return error;
    370 
    371 	if (node.sysctl_num == flash_cachesync_nodenum) {
    372 		if (t <= 0 || t > 60)
    373 			return EINVAL;
    374 	} else {
    375 		return EINVAL;
    376 	}
    377 
    378 	*(int *)rnode->sysctl_data = t;
    379 
    380 	return 0;
    381 }
    382 
    383 SYSCTL_SETUP(sysctl_flash, "sysctl flash subtree setup")
    384 {
    385 	int rc, flash_root_num;
    386 	const struct sysctlnode *node;
    387 
    388 	if ((rc = sysctl_createv(clog, 0, NULL, NULL,
    389 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
    390 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
    391 		goto error;
    392 	}
    393 
    394 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    395 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "flash",
    396 	    SYSCTL_DESCR("FLASH driver controls"),
    397 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
    398 		goto error;
    399 	}
    400 
    401 	flash_root_num = node->sysctl_num;
    402 
    403 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    404 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    405 	    CTLTYPE_INT, "cache_sync_timeout",
    406 	    SYSCTL_DESCR("FLASH write cache sync timeout in seconds"),
    407 	    sysctl_flash_verify, 0, &flash_cachesync_timeout,
    408 	    0, CTL_HW, flash_root_num, CTL_CREATE,
    409 	    CTL_EOL)) != 0) {
    410 		goto error;
    411 	}
    412 
    413 	flash_cachesync_nodenum = node->sysctl_num;
    414 
    415 	return;
    416 
    417 error:
    418 	aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
    419 }
    420