Home | History | Annotate | Line # | Download | only in flash
      1 /*	$NetBSD: flash_io.c,v 1.7 2025/01/08 11:39:50 andvar 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.7 2025/01/08 11:39:50 andvar 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 through 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, device_t dev,
    100     struct flash_interface *flash_if)
    101 {
    102 	int error;
    103 
    104 	FLDPRINTF(("starting flash io thread\n"));
    105 
    106 	fio->fio_dev = dev;
    107 	fio->fio_if = flash_if;
    108 
    109 	fio->fio_data = kmem_alloc(fio->fio_if->erasesize, KM_SLEEP);
    110 
    111 	mutex_init(&fio->fio_lock, MUTEX_DEFAULT, IPL_NONE);
    112 	cv_init(&fio->fio_cv, "flashcv");
    113 
    114 	error = bufq_alloc(&fio->fio_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
    115 	if (error)
    116 		goto err_bufq;
    117 
    118 	fio->fio_exiting = false;
    119 	fio->fio_write_pending = false;
    120 
    121 	/* arrange to allocate the kthread */
    122 	error = kthread_create(PRI_NONE, KTHREAD_MUSTJOIN | KTHREAD_MPSAFE,
    123 	    NULL, flash_sync_thread, fio, &fio->fio_thread, "flashio");
    124 
    125 	if (!error)
    126 		return 0;
    127 
    128 	bufq_free(fio->fio_bufq);
    129 err_bufq:
    130 	cv_destroy(&fio->fio_cv);
    131 	mutex_destroy(&fio->fio_lock);
    132 	kmem_free(fio->fio_data, fio->fio_if->erasesize);
    133 
    134 	return error;
    135 }
    136 
    137 void
    138 flash_sync_thread_destroy(struct flash_io *fio)
    139 {
    140 	FLDPRINTF(("stopping flash io thread\n"));
    141 
    142 	mutex_enter(&fio->fio_lock);
    143 
    144 	fio->fio_exiting = true;
    145 	cv_broadcast(&fio->fio_cv);
    146 
    147 	mutex_exit(&fio->fio_lock);
    148 
    149 	kthread_join(fio->fio_thread);
    150 
    151 	kmem_free(fio->fio_data, fio->fio_if->erasesize);
    152 	bufq_free(fio->fio_bufq);
    153 	mutex_destroy(&fio->fio_lock);
    154 	cv_destroy(&fio->fio_cv);
    155 }
    156 
    157 int
    158 flash_io_submit(struct flash_io *fio, struct buf *bp)
    159 {
    160 	FLDPRINTF(("submitting job to flash io thread: %p\n", bp));
    161 
    162 	if (__predict_false(fio->fio_exiting)) {
    163 		flash_io_done(fio, bp, ENODEV);
    164 		return ENODEV;
    165 	}
    166 
    167 	if (BUF_ISREAD(bp)) {
    168 		FLDPRINTF(("we have a read job\n"));
    169 
    170 		mutex_enter(&fio->fio_lock);
    171 		if (fio->fio_write_pending)
    172 			flash_io_cache_sync(fio);
    173 		mutex_exit(&fio->fio_lock);
    174 
    175 		flash_io_read(fio, bp);
    176 	} else {
    177 		FLDPRINTF(("we have a write job\n"));
    178 
    179 		flash_io_write(fio, bp);
    180 	}
    181 	return 0;
    182 }
    183 
    184 int
    185 flash_io_cache_write(struct flash_io *fio, flash_addr_t block, struct buf *bp)
    186 {
    187 	size_t retlen;
    188 	flash_addr_t base, offset;
    189 	int error;
    190 
    191 	KASSERT(mutex_owned(&fio->fio_lock));
    192 	KASSERT(fio->fio_if->erasesize != 0);
    193 
    194 	base = block * fio->fio_if->erasesize;
    195 	offset = bp->b_rawblkno * DEV_BSIZE - base;
    196 
    197 	FLDPRINTF(("io cache write, offset: %jd\n", (intmax_t )offset));
    198 
    199 	if (!fio->fio_write_pending) {
    200 		fio->fio_block = block;
    201 		/*
    202 		 * fill the cache with data from flash,
    203 		 * so we dont have to bother with gaps later
    204 		 */
    205 		FLDPRINTF(("filling buffer from offset %ju\n", (uintmax_t)base));
    206 		error = fio->fio_if->read(fio->fio_dev,
    207 		    base, fio->fio_if->erasesize,
    208 		    &retlen, fio->fio_data);
    209 		FLDPRINTF(("cache filled\n"));
    210 
    211 		if (error)
    212 			return error;
    213 
    214 		fio->fio_write_pending = true;
    215 		/* save creation time for aging */
    216 		binuptime(&fio->fio_creation);
    217 	}
    218 	/* copy data to cache */
    219 	memcpy(fio->fio_data + offset, bp->b_data, bp->b_resid);
    220 	bufq_put(fio->fio_bufq, bp);
    221 
    222 	/* update timestamp */
    223 	binuptime(&fio->fio_last_write);
    224 
    225 	return 0;
    226 }
    227 
    228 void
    229 flash_io_cache_sync(struct flash_io *fio)
    230 {
    231 	struct flash_erase_instruction ei;
    232 	struct buf *bp;
    233 	size_t retlen;
    234 	flash_addr_t base;
    235 	int error;
    236 
    237 	KASSERT(mutex_owned(&fio->fio_lock));
    238 
    239 	if (!fio->fio_write_pending) {
    240 		FLDPRINTF(("trying to sync with an invalid buffer\n"));
    241 		return;
    242 	}
    243 
    244 	base = fio->fio_block * fio->fio_if->erasesize;
    245 
    246 	FLDPRINTF(("erasing block at 0x%jx\n", (uintmax_t )base));
    247 	ei.ei_addr = base;
    248 	ei.ei_len = fio->fio_if->erasesize;
    249 	ei.ei_callback = NULL;
    250 	error = fio->fio_if->erase(fio->fio_dev, &ei);
    251 
    252 	if (error) {
    253 		aprint_error_dev(fio->fio_dev, "cannot erase flash flash!\n");
    254 		goto out;
    255 	}
    256 
    257 	FLDPRINTF(("writing %" PRIu32 " bytes to 0x%jx\n",
    258 		fio->fio_if->erasesize, (uintmax_t )base));
    259 
    260 	error = fio->fio_if->write(fio->fio_dev,
    261 	    base, fio->fio_if->erasesize, &retlen, fio->fio_data);
    262 
    263 	if (error || retlen != fio->fio_if->erasesize) {
    264 		aprint_error_dev(fio->fio_dev, "can't sync write cache: %d\n", error);
    265 		goto out;
    266 	}
    267 
    268 out:
    269 	while ((bp = bufq_get(fio->fio_bufq)) != NULL)
    270 		flash_io_done(fio, bp, error);
    271 
    272 	fio->fio_block = -1;
    273 	fio->fio_write_pending = false;
    274 }
    275 
    276 void
    277 flash_sync_thread(void * arg)
    278 {
    279 	struct flash_io *fio = arg;
    280 	struct bintime now;
    281 
    282 	mutex_enter(&fio->fio_lock);
    283 
    284 	while (!fio->fio_exiting) {
    285 		cv_timedwait_sig(&fio->fio_cv, &fio->fio_lock, hz / 4);
    286 		if (!fio->fio_write_pending) {
    287 			continue;
    288 		}
    289 		/* see if the cache is older than 3 seconds (safety limit),
    290 		 * or if we havent touched the cache since more than 1 ms
    291 		 */
    292 		binuptime(&now);
    293 		if (flash_timestamp_diff(&now, &fio->fio_last_write) > hz / 5) {
    294 			FLDPRINTF(("syncing write cache after timeout\n"));
    295 			flash_io_cache_sync(fio);
    296 		} else if (flash_timestamp_diff(&now, &fio->fio_creation)
    297 		    > 3 * hz) {
    298 			aprint_error_dev(fio->fio_dev,
    299 			    "syncing write cache after 3 sec timeout!\n");
    300 			flash_io_cache_sync(fio);
    301 		}
    302 	}
    303 
    304 	mutex_exit(&fio->fio_lock);
    305 
    306 	kthread_exit(0);
    307 }
    308 
    309 void
    310 flash_io_read(struct flash_io *fio, struct buf *bp)
    311 {
    312 	size_t retlen;
    313 	flash_addr_t offset;
    314 	int error;
    315 
    316 	FLDPRINTF(("flash io read\n"));
    317 
    318 	offset = bp->b_rawblkno * DEV_BSIZE;
    319 
    320 	error = fio->fio_if->read(fio->fio_dev, offset, bp->b_resid,
    321 	    &retlen, bp->b_data);
    322 
    323 	flash_io_done(fio, bp, error);
    324 }
    325 
    326 void
    327 flash_io_write(struct flash_io *fio, struct buf *bp)
    328 {
    329 	flash_addr_t block;
    330 
    331 	FLDPRINTF(("flash io write\n"));
    332 
    333 	block = flash_io_getblock(fio, bp);
    334 	FLDPRINTF(("write to block %jd\n", (intmax_t )block));
    335 
    336 	mutex_enter(&fio->fio_lock);
    337 
    338 	if (fio->fio_write_pending && fio->fio_block != block) {
    339 		FLDPRINTF(("writing to new block, syncing caches\n"));
    340 		flash_io_cache_sync(fio);
    341 	}
    342 
    343 	flash_io_cache_write(fio, block, bp);
    344 
    345 	mutex_exit(&fio->fio_lock);
    346 }
    347 
    348 void
    349 flash_io_done(struct flash_io *fio, struct buf *bp, int error)
    350 {
    351 	FLDPRINTF(("io done: %p\n", bp));
    352 
    353 	if (error == 0)
    354 		bp->b_resid = 0;
    355 
    356 	bp->b_error = error;
    357 	biodone(bp);
    358 }
    359 
    360 static int
    361 sysctl_flash_verify(SYSCTLFN_ARGS)
    362 {
    363 	int error, t;
    364 	struct sysctlnode node;
    365 
    366 	node = *rnode;
    367 	t = *(int *)rnode->sysctl_data;
    368 	node.sysctl_data = &t;
    369 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    370 	if (error || newp == NULL)
    371 		return error;
    372 
    373 	if (node.sysctl_num == flash_cachesync_nodenum) {
    374 		if (t <= 0 || t > 60)
    375 			return EINVAL;
    376 	} else {
    377 		return EINVAL;
    378 	}
    379 
    380 	*(int *)rnode->sysctl_data = t;
    381 
    382 	return 0;
    383 }
    384 
    385 SYSCTL_SETUP(sysctl_flash, "sysctl flash subtree setup")
    386 {
    387 	int rc, flash_root_num;
    388 	const struct sysctlnode *node;
    389 
    390 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    391 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "flash",
    392 	    SYSCTL_DESCR("FLASH driver controls"),
    393 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
    394 		goto error;
    395 	}
    396 
    397 	flash_root_num = node->sysctl_num;
    398 
    399 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    400 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    401 	    CTLTYPE_INT, "cache_sync_timeout",
    402 	    SYSCTL_DESCR("FLASH write cache sync timeout in seconds"),
    403 	    sysctl_flash_verify, 0, &flash_cachesync_timeout,
    404 	    0, CTL_HW, flash_root_num, CTL_CREATE,
    405 	    CTL_EOL)) != 0) {
    406 		goto error;
    407 	}
    408 
    409 	flash_cachesync_nodenum = node->sysctl_num;
    410 
    411 	return;
    412 
    413 error:
    414 	aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
    415 }
    416