xref: /illumos-gate/usr/src/uts/common/fs/fdbuffer.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1998,2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  *
26  */
27 
28 #include <sys/types.h>
29 #include <sys/cmn_err.h>
30 #include <sys/kmem.h>
31 #include <sys/systm.h>
32 #include <sys/debug.h>
33 #include <sys/ddi.h>
34 
35 #include <sys/fdbuffer.h>
36 
37 #ifdef DEBUG
38 static int fdb_debug;
39 #define	FDB_D_CREATE	001
40 #define	FDB_D_ALLOC	002
41 #define	FDB_D_IO	004
42 #define	FDB_D_ASYNC	010
43 #define	DEBUGF(lvl, args)	{ if ((lvl) & fdb_debug) cmn_err args; }
44 #else
45 #define	DEBUGF(level, args)
46 #endif
47 static struct kmem_cache *fdb_cache;
48 static void fdb_zero_holes(fdbuffer_t *fdb);
49 
50 /* ARGSUSED */
51 static int
52 fdb_cache_constructor(void *buf, void *cdrarg, int kmflags)
53 {
54 	fdbuffer_t *fdb = buf;
55 
56 	mutex_init(&fdb->fd_mutex, NULL, MUTEX_DEFAULT, NULL);
57 
58 	return (0);
59 }
60 
61 /* ARGSUSED */
62 static void
63 fdb_cache_destructor(void *buf, void *cdrarg)
64 {
65 	fdbuffer_t *fdb = buf;
66 
67 	mutex_destroy(&fdb->fd_mutex);
68 }
69 
70 void
71 fdb_init()
72 {
73 	fdb_cache = kmem_cache_create("fdb_cache", sizeof (fdbuffer_t),
74 	    0, fdb_cache_constructor, fdb_cache_destructor,
75 	    NULL, NULL, NULL, 0);
76 }
77 
78 static void
79 fdb_prepare(fdbuffer_t *fdb)
80 {
81 	fdb->fd_holes = NULL;
82 	fdb->fd_iofunc = NULL;
83 	fdb->fd_iargp = NULL;
84 	fdb->fd_parentbp = NULL;
85 	fdb->fd_resid = 0;
86 	fdb->fd_iocount = 0;
87 	fdb->fd_iodispatch = 0;
88 	fdb->fd_err = 0;
89 }
90 
91 fdbuffer_t *
92 fdb_page_create(page_t *pp, size_t len, int flags)
93 {
94 	fdbuffer_t *fdb;
95 
96 	DEBUGF(FDB_D_CREATE, (CE_NOTE,
97 	    "?fdb_page_create: pp: %p len: %lux flags: %x",
98 	    (void *)pp, len, flags));
99 
100 	ASSERT(flags & (FDB_READ|FDB_WRITE));
101 
102 	fdb = kmem_cache_alloc(fdb_cache, KM_SLEEP);
103 
104 	fdb_prepare(fdb);
105 
106 	fdb->fd_type = FDB_PAGEIO;
107 	fdb->fd_len = len;
108 	fdb->fd_state = flags;
109 	fdb->fd_pages = pp;
110 
111 	return (fdb);
112 }
113 
114 fdbuffer_t *
115 fdb_addr_create(
116 	caddr_t addr,
117 	size_t len,
118 	int flags,
119 	page_t **pplist,
120 	struct proc *procp)
121 {
122 	fdbuffer_t *fdb;
123 
124 	DEBUGF(FDB_D_CREATE, (CE_NOTE,
125 	    "?fdb_addr_create: addr: %p len: %lux flags: %x",
126 	    (void *)addr, len, flags));
127 
128 	ASSERT(flags & (FDB_READ|FDB_WRITE));
129 
130 	fdb = kmem_cache_alloc(fdb_cache, KM_SLEEP);
131 
132 	fdb_prepare(fdb);
133 
134 	fdb->fd_type = FDB_VADDR;
135 	fdb->fd_len = len;
136 	fdb->fd_state = flags;
137 	fdb->fd_addr = addr;
138 	fdb->fd_shadow = pplist;
139 	fdb->fd_procp = procp;
140 
141 	return (fdb);
142 }
143 
144 void
145 fdb_set_iofunc(fdbuffer_t *fdb, fdb_iodone_t iofunc, void *ioargp, int flag)
146 {
147 	ASSERT(fdb);
148 	ASSERT(iofunc);
149 	ASSERT((flag & ~FDB_ICALLBACK) == 0);
150 
151 	fdb->fd_iofunc = iofunc;
152 	fdb->fd_iargp = ioargp;
153 
154 	mutex_enter(&fdb->fd_mutex);
155 
156 	if (flag & FDB_ICALLBACK)
157 		fdb->fd_state |= FDB_ICALLBACK;
158 
159 	fdb->fd_state |= FDB_ASYNC;
160 
161 	mutex_exit(&fdb->fd_mutex);
162 }
163 
164 int
165 fdb_get_error(fdbuffer_t *fdb)
166 {
167 	return (fdb->fd_err);
168 }
169 
170 void
171 fdb_free(fdbuffer_t *fdb)
172 {
173 	fdb_holes_t *fdh, *fdhp;
174 
175 	DEBUGF(FDB_D_CREATE, (CE_NOTE, "?fdb_free: addr: %p flags: %x",
176 	    (void *)fdb, fdb->fd_state));
177 
178 	ASSERT(fdb);
179 	ASSERT(fdb->fd_iodispatch == 0);
180 
181 	if (fdb->fd_state & FDB_ZEROHOLE) {
182 		fdb_zero_holes(fdb);
183 	}
184 
185 	for (fdh = fdb->fd_holes; fdh; ) {
186 		fdhp = fdh;
187 		fdh = fdh->next_hole;
188 		kmem_free(fdhp, sizeof (fdb_holes_t));
189 	}
190 
191 	if (fdb->fd_parentbp != NULL) {
192 		switch (fdb->fd_type) {
193 		case FDB_PAGEIO:
194 			pageio_done(fdb->fd_parentbp);
195 			break;
196 		case FDB_VADDR:
197 			kmem_free(fdb->fd_parentbp, sizeof (struct buf));
198 			break;
199 		default:
200 			cmn_err(CE_CONT, "?fdb_free: Unknown fdb type.");
201 			break;
202 		}
203 	}
204 
205 	kmem_cache_free(fdb_cache, fdb);
206 
207 }
208 
209 /*
210  * The offset should be from the begining of the buffer
211  * it has nothing to do with file offset. This fact should be
212  * reflected in the caller of this routine.
213  */
214 
215 void
216 fdb_add_hole(fdbuffer_t *fdb, u_offset_t off, size_t len)
217 {
218 	fdb_holes_t *this_hole;
219 
220 	ASSERT(fdb);
221 	ASSERT(off < fdb->fd_len);
222 
223 	DEBUGF(FDB_D_IO, (CE_NOTE, "?fdb_add_hole: off %llx len %lx",
224 	    off, len));
225 
226 	this_hole = kmem_alloc(sizeof (fdb_holes_t), KM_SLEEP);
227 	this_hole->off = off;
228 	this_hole->len = len;
229 
230 	if (fdb->fd_holes == NULL || off < fdb->fd_holes->off) {
231 		this_hole->next_hole = fdb->fd_holes;
232 		fdb->fd_holes = this_hole;
233 	} else {
234 		fdb_holes_t *fdhp = fdb->fd_holes;
235 
236 		while (fdhp->next_hole && off > fdhp->next_hole->off)
237 			fdhp = fdhp->next_hole;
238 
239 		this_hole->next_hole = fdhp->next_hole;
240 		fdhp->next_hole = this_hole;
241 	}
242 
243 	mutex_enter(&fdb->fd_mutex);
244 
245 	fdb->fd_iocount += len;
246 
247 	mutex_exit(&fdb->fd_mutex);
248 }
249 
250 fdb_holes_t *
251 fdb_get_holes(fdbuffer_t *fdb)
252 {
253 	ASSERT(fdb);
254 
255 	if (fdb->fd_state & FDB_ZEROHOLE) {
256 		fdb_zero_holes(fdb);
257 	}
258 
259 	return (fdb->fd_holes);
260 }
261 
262 /*
263  * Note that offsets refer to offsets from the begining of the buffer
264  * and as such the memory should be cleared accordingly.
265  */
266 
267 static void
268 fdb_zero_holes(fdbuffer_t *fdb)
269 {
270 	fdb_holes_t *fdh = fdb->fd_holes;
271 	page_t *pp;
272 
273 	ASSERT(fdb);
274 
275 	if (!fdh)
276 		return;
277 
278 	switch (fdb->fd_type) {
279 	case FDB_PAGEIO:
280 		pp = fdb->fd_pages;
281 		while (fdh) {
282 			fdb_holes_t *pfdh = fdh;
283 			size_t l = fdh->len;
284 			u_offset_t o = fdh->off;
285 			ASSERT(pp);
286 
287 			do {
288 				int  zerolen;
289 				ASSERT(o >= pp->p_offset);
290 
291 				/*
292 				 * This offset is wrong since
293 				 * the offset passed from the pages
294 				 * perspective starts at some virtual
295 				 * address but the hole is relative
296 				 * to the beginning of the fdbuffer.
297 				 */
298 				if (o >= pp->p_offset + PAGESIZE)
299 					continue;
300 
301 				zerolen = min(PAGESIZE, l);
302 
303 				ASSERT(zerolen > 0);
304 				ASSERT(zerolen <= PAGESIZE);
305 
306 				pagezero(pp, ((uintptr_t)o & PAGEOFFSET),
307 				    zerolen);
308 
309 				l -= zerolen;
310 				o += zerolen;
311 
312 				if (l == 0)
313 					break;
314 
315 			} while (pp = page_list_next(pp));
316 
317 			if (!pp)
318 				break;
319 
320 			fdh = fdh->next_hole;
321 			kmem_free(pfdh, sizeof (fdb_holes_t));
322 		}
323 		break;
324 	case FDB_VADDR:
325 		while (fdh) {
326 			fdb_holes_t *pfdh = fdh;
327 
328 			bzero(fdb->fd_addr + fdh->off, fdh->len);
329 
330 			fdh = fdh->next_hole;
331 			kmem_free(pfdh, sizeof (fdb_holes_t));
332 		}
333 		break;
334 	default:
335 		panic("fdb_zero_holes: Unknown fdb type.");
336 		break;
337 	}
338 }
339 
340 
341 buf_t *
342 fdb_iosetup(fdbuffer_t *fdb, u_offset_t off, size_t len, struct vnode *vp,
343     int b_flags)
344 {
345 	buf_t *bp;
346 
347 	DEBUGF(FDB_D_IO, (CE_NOTE,
348 	    "?fdb_iosetup: off: %llx len: %lux fdb: len: %lux flags: %x",
349 	    off, len, fdb->fd_len, fdb->fd_state));
350 
351 	ASSERT(fdb);
352 
353 	mutex_enter(&fdb->fd_mutex);
354 
355 	ASSERT(((b_flags & B_READ) && (fdb->fd_state & FDB_READ)) ||
356 	    ((b_flags & B_WRITE) && (fdb->fd_state & FDB_WRITE)));
357 	/*
358 	 * The fdb can be used either in sync or async mode, if the
359 	 * buffer has not been used it may be used in either mode, but
360 	 * once you have started to use the buf in either mode all
361 	 * subsequent i/o requests must take place the same way.
362 	 */
363 
364 	ASSERT(((b_flags & B_ASYNC) &&
365 	    ((fdb->fd_state & FDB_ASYNC) || !(fdb->fd_state & FDB_SYNC))) ||
366 	    (!(b_flags & B_ASYNC) &&
367 	    ((fdb->fd_state & FDB_SYNC) || !(fdb->fd_state & FDB_ASYNC))));
368 
369 
370 	fdb->fd_state |= b_flags & B_ASYNC ? FDB_ASYNC : FDB_SYNC;
371 
372 	fdb->fd_iodispatch++;
373 
374 	ASSERT((fdb->fd_state & FDB_ASYNC && fdb->fd_iofunc != NULL) ||
375 	    fdb->fd_state & FDB_SYNC);
376 
377 	mutex_exit(&fdb->fd_mutex);
378 
379 	ASSERT((len & (DEV_BSIZE - 1)) == 0);
380 	ASSERT(off+len <= fdb->fd_len);
381 
382 	switch (fdb->fd_type) {
383 	case FDB_PAGEIO:
384 		if (fdb->fd_parentbp == NULL) {
385 			bp = pageio_setup(fdb->fd_pages, len, vp, b_flags);
386 			fdb->fd_parentbp = bp;
387 		}
388 		break;
389 	case FDB_VADDR:
390 		if (fdb->fd_parentbp == NULL) {
391 
392 			bp = kmem_alloc(sizeof (buf_t), KM_SLEEP);
393 			bioinit(bp);
394 			bp->b_error = 0;
395 			bp->b_proc = fdb->fd_procp;
396 			bp->b_flags = b_flags | B_BUSY | B_PHYS;
397 			bp->b_bcount = len;
398 			bp->b_un.b_addr = fdb->fd_addr;
399 			bp->b_shadow = fdb->fd_shadow;
400 			if (fdb->fd_shadow != NULL)
401 				bp->b_flags |= B_SHADOW;
402 			fdb->fd_parentbp = bp;
403 		}
404 		break;
405 	default:
406 		panic("fdb_iosetup: Unsupported fdb type.");
407 		break;
408 	};
409 
410 	bp = bioclone(fdb->fd_parentbp, off, len, 0, 0,
411 	    (b_flags & B_ASYNC) ? fdb_iodone : NULL,
412 	    NULL, KM_SLEEP);
413 
414 	bp->b_forw = (struct buf *)fdb;
415 
416 	if (b_flags & B_ASYNC)
417 		bp->b_flags |= B_ASYNC;
418 
419 	return (bp);
420 }
421 
422 size_t
423 fdb_get_iolen(fdbuffer_t *fdb)
424 {
425 	ASSERT(fdb);
426 	ASSERT(fdb->fd_iodispatch == 0);
427 
428 	return (fdb->fd_iocount - fdb->fd_resid);
429 }
430 
431 void
432 fdb_ioerrdone(fdbuffer_t *fdb, int error)
433 {
434 	ASSERT(fdb);
435 	ASSERT(fdb->fd_state & FDB_ASYNC);
436 
437 	DEBUGF(FDB_D_IO, (CE_NOTE,
438 	    "?fdb_ioerrdone: fdb: len: %lux flags: %x error: %d",
439 	    fdb->fd_len, fdb->fd_state, error));
440 
441 	mutex_enter(&fdb->fd_mutex);
442 
443 	fdb->fd_err = error;
444 
445 	if (error)
446 		fdb->fd_state |= FDB_ERROR;
447 	else
448 		fdb->fd_state |= FDB_DONE;
449 
450 	/*
451 	 * If there is outstanding i/o return wainting for i/o's to complete.
452 	 */
453 	if (fdb->fd_iodispatch > 0) {
454 		mutex_exit(&fdb->fd_mutex);
455 		return;
456 	}
457 
458 	mutex_exit(&fdb->fd_mutex);
459 	fdb->fd_iofunc(fdb, fdb->fd_iargp, NULL);
460 }
461 
462 int
463 fdb_iodone(buf_t *bp)
464 {
465 	fdbuffer_t *fdb = (fdbuffer_t *)bp->b_forw;
466 	int	error, isasync;
467 	int	icallback;
468 
469 	ASSERT(fdb);
470 
471 	DEBUGF(FDB_D_IO, (CE_NOTE,
472 	    "?fdb_iodone: fdb: len: %lux flags: %x error: %d",
473 	    fdb->fd_len, fdb->fd_state, geterror(bp)));
474 
475 	if (bp->b_flags & B_REMAPPED)
476 		bp_mapout(bp);
477 
478 	mutex_enter(&fdb->fd_mutex);
479 
480 	icallback = fdb->fd_state & FDB_ICALLBACK;
481 	isasync = fdb->fd_state & FDB_ASYNC;
482 
483 	ASSERT(fdb->fd_iodispatch > 0);
484 	fdb->fd_iodispatch--;
485 
486 	if (error = geterror(bp)) {
487 		fdb->fd_err = error;
488 		if (bp->b_resid)
489 			fdb->fd_resid += bp->b_resid;
490 		else
491 			fdb->fd_resid += bp->b_bcount;
492 	}
493 
494 	fdb->fd_iocount += bp->b_bcount;
495 
496 	/*
497 	 * ioack collects the total amount of i/o accounted for
498 	 * this includes:
499 	 *
500 	 *	- i/o completed
501 	 *	- i/o attempted but not completed,
502 	 *	- i/o not done due to holes.
503 	 *
504 	 * Once the entire i/o ranges has been accounted for we'll
505 	 * call the async function associated with the fdb.
506 	 *
507 	 */
508 
509 	if ((fdb->fd_iodispatch == 0) &&
510 	    (fdb->fd_state & (FDB_ERROR|FDB_DONE))) {
511 
512 		mutex_exit(&fdb->fd_mutex);
513 
514 		if (isasync || icallback) {
515 			fdb->fd_iofunc(fdb, fdb->fd_iargp, bp);
516 		}
517 
518 	} else {
519 
520 		mutex_exit(&fdb->fd_mutex);
521 
522 		if (icallback) {
523 			fdb->fd_iofunc(fdb, fdb->fd_iargp, bp);
524 		}
525 	}
526 
527 	freerbuf(bp);
528 	return (0);
529 }
530