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@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