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
flash_timestamp_diff(struct bintime * bt,struct bintime * b2)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
flash_io_getblock(struct flash_io * fio,struct buf * bp)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
flash_sync_thread_init(struct flash_io * fio,device_t dev,struct flash_interface * flash_if)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
flash_sync_thread_destroy(struct flash_io * fio)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
flash_io_submit(struct flash_io * fio,struct buf * bp)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
flash_io_cache_write(struct flash_io * fio,flash_addr_t block,struct buf * bp)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
flash_io_cache_sync(struct flash_io * fio)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
flash_sync_thread(void * arg)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
flash_io_read(struct flash_io * fio,struct buf * bp)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
flash_io_write(struct flash_io * fio,struct buf * bp)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
flash_io_done(struct flash_io * fio,struct buf * bp,int error)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
sysctl_flash_verify(SYSCTLFN_ARGS)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