1*832e988bSandvar /* $NetBSD: flash_io.c,v 1.7 2025/01/08 11:39:50 andvar Exp $ */ 20e8f635bSahoka 30e8f635bSahoka /*- 40e8f635bSahoka * Copyright (c) 2011 Department of Software Engineering, 50e8f635bSahoka * University of Szeged, Hungary 60e8f635bSahoka * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org> 70e8f635bSahoka * All rights reserved. 80e8f635bSahoka * 90e8f635bSahoka * This code is derived from software contributed to The NetBSD Foundation 100e8f635bSahoka * by the Department of Software Engineering, University of Szeged, Hungary 110e8f635bSahoka * 120e8f635bSahoka * Redistribution and use in source and binary forms, with or without 130e8f635bSahoka * modification, are permitted provided that the following conditions 140e8f635bSahoka * are met: 150e8f635bSahoka * 1. Redistributions of source code must retain the above copyright 160e8f635bSahoka * notice, this list of conditions and the following disclaimer. 170e8f635bSahoka * 2. Redistributions in binary form must reproduce the above copyright 180e8f635bSahoka * notice, this list of conditions and the following disclaimer in the 190e8f635bSahoka * documentation and/or other materials provided with the distribution. 200e8f635bSahoka * 210e8f635bSahoka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 220e8f635bSahoka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 230e8f635bSahoka * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 240e8f635bSahoka * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 250e8f635bSahoka * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 260e8f635bSahoka * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 270e8f635bSahoka * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 280e8f635bSahoka * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 290e8f635bSahoka * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 300e8f635bSahoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 310e8f635bSahoka * SUCH DAMAGE. 320e8f635bSahoka */ 330e8f635bSahoka 340e8f635bSahoka #include <sys/cdefs.h> 35*832e988bSandvar __KERNEL_RCSID(0, "$NetBSD: flash_io.c,v 1.7 2025/01/08 11:39:50 andvar Exp $"); 360e8f635bSahoka 370e8f635bSahoka #include <sys/param.h> 380e8f635bSahoka #include <sys/buf.h> 390e8f635bSahoka #include <sys/bufq.h> 400e8f635bSahoka #include <sys/kernel.h> 410e8f635bSahoka #include <sys/kmem.h> 420e8f635bSahoka #include <sys/kthread.h> 430e8f635bSahoka #include <sys/mutex.h> 440e8f635bSahoka #include <sys/sysctl.h> 450e8f635bSahoka 460e8f635bSahoka #include <dev/flash/flash.h> 470e8f635bSahoka #include <dev/flash/flash_io.h> 480e8f635bSahoka 490e8f635bSahoka #ifdef FLASH_DEBUG 500e8f635bSahoka extern int flashdebug; 510e8f635bSahoka #endif 520e8f635bSahoka 530e8f635bSahoka int flash_cachesync_timeout = 1; 540e8f635bSahoka int flash_cachesync_nodenum; 550e8f635bSahoka 560e8f635bSahoka void flash_io_read(struct flash_io *, struct buf *); 570e8f635bSahoka void flash_io_write(struct flash_io *, struct buf *); 580e8f635bSahoka void flash_io_done(struct flash_io *, struct buf *, int); 590e8f635bSahoka int flash_io_cache_write(struct flash_io *, flash_addr_t, struct buf *); 600e8f635bSahoka void flash_io_cache_sync(struct flash_io *); 610e8f635bSahoka 620e8f635bSahoka static int 630e8f635bSahoka flash_timestamp_diff(struct bintime *bt, struct bintime *b2) 640e8f635bSahoka { 650e8f635bSahoka struct bintime b1 = *bt; 660e8f635bSahoka struct timeval tv; 670e8f635bSahoka 680e8f635bSahoka bintime_sub(&b1, b2); 690e8f635bSahoka bintime2timeval(&b1, &tv); 700e8f635bSahoka 710e8f635bSahoka return tvtohz(&tv); 720e8f635bSahoka } 730e8f635bSahoka 740e8f635bSahoka static flash_addr_t 750e8f635bSahoka flash_io_getblock(struct flash_io *fio, struct buf *bp) 760e8f635bSahoka { 770e8f635bSahoka flash_off_t block, last; 780e8f635bSahoka 790e8f635bSahoka /* get block number of first byte */ 800e8f635bSahoka block = bp->b_rawblkno * DEV_BSIZE / fio->fio_if->erasesize; 810e8f635bSahoka 820e8f635bSahoka /* block of the last bite */ 830e8f635bSahoka last = (bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1) 840e8f635bSahoka / fio->fio_if->erasesize; 850e8f635bSahoka 866c0defe2Sgutteridge /* spans through multiple blocks, needs special handling */ 870e8f635bSahoka if (last != block) { 880e8f635bSahoka printf("0x%jx -> 0x%jx\n", 890e8f635bSahoka bp->b_rawblkno * DEV_BSIZE, 900e8f635bSahoka bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1); 910e8f635bSahoka panic("TODO: multiple block write. last: %jd, current: %jd", 920e8f635bSahoka (intmax_t )last, (intmax_t )block); 930e8f635bSahoka } 940e8f635bSahoka 950e8f635bSahoka return block; 960e8f635bSahoka } 970e8f635bSahoka 980e8f635bSahoka int 99fb19d2b7Scliff flash_sync_thread_init(struct flash_io *fio, device_t dev, 100fb19d2b7Scliff struct flash_interface *flash_if) 1010e8f635bSahoka { 1020e8f635bSahoka int error; 1030e8f635bSahoka 1040e8f635bSahoka FLDPRINTF(("starting flash io thread\n")); 1050e8f635bSahoka 106fb19d2b7Scliff fio->fio_dev = dev; 1070e8f635bSahoka fio->fio_if = flash_if; 1080e8f635bSahoka 1090e8f635bSahoka fio->fio_data = kmem_alloc(fio->fio_if->erasesize, KM_SLEEP); 1100e8f635bSahoka 1110e8f635bSahoka mutex_init(&fio->fio_lock, MUTEX_DEFAULT, IPL_NONE); 1120e8f635bSahoka cv_init(&fio->fio_cv, "flashcv"); 1130e8f635bSahoka 1140e8f635bSahoka error = bufq_alloc(&fio->fio_bufq, "fcfs", BUFQ_SORT_RAWBLOCK); 1150e8f635bSahoka if (error) 1160e8f635bSahoka goto err_bufq; 1170e8f635bSahoka 1180e8f635bSahoka fio->fio_exiting = false; 1190e8f635bSahoka fio->fio_write_pending = false; 1200e8f635bSahoka 1210e8f635bSahoka /* arrange to allocate the kthread */ 122a0ffc02aSrmind error = kthread_create(PRI_NONE, KTHREAD_MUSTJOIN | KTHREAD_MPSAFE, 1230e8f635bSahoka NULL, flash_sync_thread, fio, &fio->fio_thread, "flashio"); 1240e8f635bSahoka 1250e8f635bSahoka if (!error) 1260e8f635bSahoka return 0; 1270e8f635bSahoka 1280e8f635bSahoka bufq_free(fio->fio_bufq); 1290e8f635bSahoka err_bufq: 1300e8f635bSahoka cv_destroy(&fio->fio_cv); 1310e8f635bSahoka mutex_destroy(&fio->fio_lock); 1320e8f635bSahoka kmem_free(fio->fio_data, fio->fio_if->erasesize); 1330e8f635bSahoka 1340e8f635bSahoka return error; 1350e8f635bSahoka } 1360e8f635bSahoka 1370e8f635bSahoka void 1380e8f635bSahoka flash_sync_thread_destroy(struct flash_io *fio) 1390e8f635bSahoka { 1400e8f635bSahoka FLDPRINTF(("stopping flash io thread\n")); 1410e8f635bSahoka 1420e8f635bSahoka mutex_enter(&fio->fio_lock); 1430e8f635bSahoka 1440e8f635bSahoka fio->fio_exiting = true; 1450e8f635bSahoka cv_broadcast(&fio->fio_cv); 1460e8f635bSahoka 1470e8f635bSahoka mutex_exit(&fio->fio_lock); 1480e8f635bSahoka 1490e8f635bSahoka kthread_join(fio->fio_thread); 1500e8f635bSahoka 1510e8f635bSahoka kmem_free(fio->fio_data, fio->fio_if->erasesize); 1520e8f635bSahoka bufq_free(fio->fio_bufq); 1530e8f635bSahoka mutex_destroy(&fio->fio_lock); 1540e8f635bSahoka cv_destroy(&fio->fio_cv); 1550e8f635bSahoka } 1560e8f635bSahoka 1570e8f635bSahoka int 1580e8f635bSahoka flash_io_submit(struct flash_io *fio, struct buf *bp) 1590e8f635bSahoka { 1600e8f635bSahoka FLDPRINTF(("submitting job to flash io thread: %p\n", bp)); 1610e8f635bSahoka 1620e8f635bSahoka if (__predict_false(fio->fio_exiting)) { 1630e8f635bSahoka flash_io_done(fio, bp, ENODEV); 1640e8f635bSahoka return ENODEV; 1650e8f635bSahoka } 1660e8f635bSahoka 1670e8f635bSahoka if (BUF_ISREAD(bp)) { 1680e8f635bSahoka FLDPRINTF(("we have a read job\n")); 1690e8f635bSahoka 1700e8f635bSahoka mutex_enter(&fio->fio_lock); 1710e8f635bSahoka if (fio->fio_write_pending) 1720e8f635bSahoka flash_io_cache_sync(fio); 1730e8f635bSahoka mutex_exit(&fio->fio_lock); 1740e8f635bSahoka 1750e8f635bSahoka flash_io_read(fio, bp); 1760e8f635bSahoka } else { 1770e8f635bSahoka FLDPRINTF(("we have a write job\n")); 1780e8f635bSahoka 1790e8f635bSahoka flash_io_write(fio, bp); 1800e8f635bSahoka } 1810e8f635bSahoka return 0; 1820e8f635bSahoka } 1830e8f635bSahoka 1840e8f635bSahoka int 1850e8f635bSahoka flash_io_cache_write(struct flash_io *fio, flash_addr_t block, struct buf *bp) 1860e8f635bSahoka { 1870e8f635bSahoka size_t retlen; 1880e8f635bSahoka flash_addr_t base, offset; 1890e8f635bSahoka int error; 1900e8f635bSahoka 1910e8f635bSahoka KASSERT(mutex_owned(&fio->fio_lock)); 1920e8f635bSahoka KASSERT(fio->fio_if->erasesize != 0); 1930e8f635bSahoka 1940e8f635bSahoka base = block * fio->fio_if->erasesize; 1950e8f635bSahoka offset = bp->b_rawblkno * DEV_BSIZE - base; 1960e8f635bSahoka 1970e8f635bSahoka FLDPRINTF(("io cache write, offset: %jd\n", (intmax_t )offset)); 1980e8f635bSahoka 1990e8f635bSahoka if (!fio->fio_write_pending) { 2000e8f635bSahoka fio->fio_block = block; 2010e8f635bSahoka /* 2020e8f635bSahoka * fill the cache with data from flash, 2030e8f635bSahoka * so we dont have to bother with gaps later 2040e8f635bSahoka */ 2050e8f635bSahoka FLDPRINTF(("filling buffer from offset %ju\n", (uintmax_t)base)); 2060e8f635bSahoka error = fio->fio_if->read(fio->fio_dev, 2070e8f635bSahoka base, fio->fio_if->erasesize, 2080e8f635bSahoka &retlen, fio->fio_data); 2090e8f635bSahoka FLDPRINTF(("cache filled\n")); 2100e8f635bSahoka 2110e8f635bSahoka if (error) 2120e8f635bSahoka return error; 2130e8f635bSahoka 2140e8f635bSahoka fio->fio_write_pending = true; 2150e8f635bSahoka /* save creation time for aging */ 2160e8f635bSahoka binuptime(&fio->fio_creation); 2170e8f635bSahoka } 2180e8f635bSahoka /* copy data to cache */ 2190e8f635bSahoka memcpy(fio->fio_data + offset, bp->b_data, bp->b_resid); 2200e8f635bSahoka bufq_put(fio->fio_bufq, bp); 2210e8f635bSahoka 2220e8f635bSahoka /* update timestamp */ 2230e8f635bSahoka binuptime(&fio->fio_last_write); 2240e8f635bSahoka 2250e8f635bSahoka return 0; 2260e8f635bSahoka } 2270e8f635bSahoka 2280e8f635bSahoka void 2290e8f635bSahoka flash_io_cache_sync(struct flash_io *fio) 2300e8f635bSahoka { 2310e8f635bSahoka struct flash_erase_instruction ei; 2320e8f635bSahoka struct buf *bp; 2330e8f635bSahoka size_t retlen; 2340e8f635bSahoka flash_addr_t base; 2350e8f635bSahoka int error; 2360e8f635bSahoka 2370e8f635bSahoka KASSERT(mutex_owned(&fio->fio_lock)); 2380e8f635bSahoka 2390e8f635bSahoka if (!fio->fio_write_pending) { 2400e8f635bSahoka FLDPRINTF(("trying to sync with an invalid buffer\n")); 2410e8f635bSahoka return; 2420e8f635bSahoka } 2430e8f635bSahoka 2440e8f635bSahoka base = fio->fio_block * fio->fio_if->erasesize; 2450e8f635bSahoka 246*832e988bSandvar FLDPRINTF(("erasing block at 0x%jx\n", (uintmax_t )base)); 2470e8f635bSahoka ei.ei_addr = base; 2480e8f635bSahoka ei.ei_len = fio->fio_if->erasesize; 2490e8f635bSahoka ei.ei_callback = NULL; 2500e8f635bSahoka error = fio->fio_if->erase(fio->fio_dev, &ei); 2510e8f635bSahoka 2520e8f635bSahoka if (error) { 2530e8f635bSahoka aprint_error_dev(fio->fio_dev, "cannot erase flash flash!\n"); 2540e8f635bSahoka goto out; 2550e8f635bSahoka } 2560e8f635bSahoka 257df8b0211Sahoka FLDPRINTF(("writing %" PRIu32 " bytes to 0x%jx\n", 2580e8f635bSahoka fio->fio_if->erasesize, (uintmax_t )base)); 2590e8f635bSahoka 2600e8f635bSahoka error = fio->fio_if->write(fio->fio_dev, 2610e8f635bSahoka base, fio->fio_if->erasesize, &retlen, fio->fio_data); 2620e8f635bSahoka 2630e8f635bSahoka if (error || retlen != fio->fio_if->erasesize) { 2640e8f635bSahoka aprint_error_dev(fio->fio_dev, "can't sync write cache: %d\n", error); 2650e8f635bSahoka goto out; 2660e8f635bSahoka } 2670e8f635bSahoka 2680e8f635bSahoka out: 2690e8f635bSahoka while ((bp = bufq_get(fio->fio_bufq)) != NULL) 2700e8f635bSahoka flash_io_done(fio, bp, error); 2710e8f635bSahoka 2720e8f635bSahoka fio->fio_block = -1; 2730e8f635bSahoka fio->fio_write_pending = false; 2740e8f635bSahoka } 2750e8f635bSahoka 2760e8f635bSahoka void 2770e8f635bSahoka flash_sync_thread(void * arg) 2780e8f635bSahoka { 2790e8f635bSahoka struct flash_io *fio = arg; 2800e8f635bSahoka struct bintime now; 2810e8f635bSahoka 2820e8f635bSahoka mutex_enter(&fio->fio_lock); 2830e8f635bSahoka 2840e8f635bSahoka while (!fio->fio_exiting) { 2850e8f635bSahoka cv_timedwait_sig(&fio->fio_cv, &fio->fio_lock, hz / 4); 2860e8f635bSahoka if (!fio->fio_write_pending) { 2870e8f635bSahoka continue; 2880e8f635bSahoka } 2890e8f635bSahoka /* see if the cache is older than 3 seconds (safety limit), 2900e8f635bSahoka * or if we havent touched the cache since more than 1 ms 2910e8f635bSahoka */ 2920e8f635bSahoka binuptime(&now); 2930e8f635bSahoka if (flash_timestamp_diff(&now, &fio->fio_last_write) > hz / 5) { 2940e8f635bSahoka FLDPRINTF(("syncing write cache after timeout\n")); 2950e8f635bSahoka flash_io_cache_sync(fio); 2960e8f635bSahoka } else if (flash_timestamp_diff(&now, &fio->fio_creation) 2970e8f635bSahoka > 3 * hz) { 2980e8f635bSahoka aprint_error_dev(fio->fio_dev, 2990e8f635bSahoka "syncing write cache after 3 sec timeout!\n"); 3000e8f635bSahoka flash_io_cache_sync(fio); 3010e8f635bSahoka } 3020e8f635bSahoka } 3030e8f635bSahoka 3040e8f635bSahoka mutex_exit(&fio->fio_lock); 3050e8f635bSahoka 3060e8f635bSahoka kthread_exit(0); 3070e8f635bSahoka } 3080e8f635bSahoka 3090e8f635bSahoka void 3100e8f635bSahoka flash_io_read(struct flash_io *fio, struct buf *bp) 3110e8f635bSahoka { 3120e8f635bSahoka size_t retlen; 3130e8f635bSahoka flash_addr_t offset; 3140e8f635bSahoka int error; 3150e8f635bSahoka 3160e8f635bSahoka FLDPRINTF(("flash io read\n")); 3170e8f635bSahoka 3180e8f635bSahoka offset = bp->b_rawblkno * DEV_BSIZE; 3190e8f635bSahoka 3200e8f635bSahoka error = fio->fio_if->read(fio->fio_dev, offset, bp->b_resid, 3210e8f635bSahoka &retlen, bp->b_data); 3220e8f635bSahoka 3230e8f635bSahoka flash_io_done(fio, bp, error); 3240e8f635bSahoka } 3250e8f635bSahoka 3260e8f635bSahoka void 3270e8f635bSahoka flash_io_write(struct flash_io *fio, struct buf *bp) 3280e8f635bSahoka { 3290e8f635bSahoka flash_addr_t block; 3300e8f635bSahoka 3310e8f635bSahoka FLDPRINTF(("flash io write\n")); 3320e8f635bSahoka 3330e8f635bSahoka block = flash_io_getblock(fio, bp); 3340e8f635bSahoka FLDPRINTF(("write to block %jd\n", (intmax_t )block)); 3350e8f635bSahoka 3360e8f635bSahoka mutex_enter(&fio->fio_lock); 3370e8f635bSahoka 3380e8f635bSahoka if (fio->fio_write_pending && fio->fio_block != block) { 3390e8f635bSahoka FLDPRINTF(("writing to new block, syncing caches\n")); 3400e8f635bSahoka flash_io_cache_sync(fio); 3410e8f635bSahoka } 3420e8f635bSahoka 3430e8f635bSahoka flash_io_cache_write(fio, block, bp); 3440e8f635bSahoka 3450e8f635bSahoka mutex_exit(&fio->fio_lock); 3460e8f635bSahoka } 3470e8f635bSahoka 3480e8f635bSahoka void 3490e8f635bSahoka flash_io_done(struct flash_io *fio, struct buf *bp, int error) 3500e8f635bSahoka { 3510e8f635bSahoka FLDPRINTF(("io done: %p\n", bp)); 3520e8f635bSahoka 3530e8f635bSahoka if (error == 0) 3540e8f635bSahoka bp->b_resid = 0; 3550e8f635bSahoka 3560e8f635bSahoka bp->b_error = error; 3570e8f635bSahoka biodone(bp); 3580e8f635bSahoka } 3590e8f635bSahoka 3600e8f635bSahoka static int 3610e8f635bSahoka sysctl_flash_verify(SYSCTLFN_ARGS) 3620e8f635bSahoka { 3630e8f635bSahoka int error, t; 3640e8f635bSahoka struct sysctlnode node; 3650e8f635bSahoka 3660e8f635bSahoka node = *rnode; 3670e8f635bSahoka t = *(int *)rnode->sysctl_data; 3680e8f635bSahoka node.sysctl_data = &t; 3690e8f635bSahoka error = sysctl_lookup(SYSCTLFN_CALL(&node)); 3700e8f635bSahoka if (error || newp == NULL) 3710e8f635bSahoka return error; 3720e8f635bSahoka 3730e8f635bSahoka if (node.sysctl_num == flash_cachesync_nodenum) { 3740e8f635bSahoka if (t <= 0 || t > 60) 3750e8f635bSahoka return EINVAL; 3760e8f635bSahoka } else { 3770e8f635bSahoka return EINVAL; 3780e8f635bSahoka } 3790e8f635bSahoka 3800e8f635bSahoka *(int *)rnode->sysctl_data = t; 3810e8f635bSahoka 3820e8f635bSahoka return 0; 3830e8f635bSahoka } 3840e8f635bSahoka 3850e8f635bSahoka SYSCTL_SETUP(sysctl_flash, "sysctl flash subtree setup") 3860e8f635bSahoka { 3870e8f635bSahoka int rc, flash_root_num; 3880e8f635bSahoka const struct sysctlnode *node; 3890e8f635bSahoka 3900e8f635bSahoka if ((rc = sysctl_createv(clog, 0, NULL, &node, 3910e8f635bSahoka CTLFLAG_PERMANENT, CTLTYPE_NODE, "flash", 3920e8f635bSahoka SYSCTL_DESCR("FLASH driver controls"), 3930e8f635bSahoka NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) { 3940e8f635bSahoka goto error; 3950e8f635bSahoka } 3960e8f635bSahoka 3970e8f635bSahoka flash_root_num = node->sysctl_num; 3980e8f635bSahoka 3990e8f635bSahoka if ((rc = sysctl_createv(clog, 0, NULL, &node, 4000e8f635bSahoka CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 4010e8f635bSahoka CTLTYPE_INT, "cache_sync_timeout", 4020e8f635bSahoka SYSCTL_DESCR("FLASH write cache sync timeout in seconds"), 4030e8f635bSahoka sysctl_flash_verify, 0, &flash_cachesync_timeout, 4040e8f635bSahoka 0, CTL_HW, flash_root_num, CTL_CREATE, 4050e8f635bSahoka CTL_EOL)) != 0) { 4060e8f635bSahoka goto error; 4070e8f635bSahoka } 4080e8f635bSahoka 4090e8f635bSahoka flash_cachesync_nodenum = node->sysctl_num; 4100e8f635bSahoka 4110e8f635bSahoka return; 4120e8f635bSahoka 4130e8f635bSahoka error: 4140e8f635bSahoka aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); 4150e8f635bSahoka } 416