xref: /netbsd-src/sys/dev/flash/flash_io.c (revision 88fcb00c0357f2d7c1774f86a352637bfda96184)
1 /*	$NetBSD: flash_io.c,v 1.2 2011/06/28 20:58:00 ahoka 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.2 2011/06/28 20:58:00 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 %" PRIu32 " 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