|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ ! 26: /* ! 27: * Copyright (c) 1993 ! 28: * The Regents of the University of California. All rights reserved. ! 29: * ! 30: * Redistribution and use in source and binary forms, with or without ! 31: * modification, are permitted provided that the following conditions ! 32: * are met: ! 33: * 1. Redistributions of source code must retain the above copyright ! 34: * notice, this list of conditions and the following disclaimer. ! 35: * 2. Redistributions in binary form must reproduce the above copyright ! 36: * notice, this list of conditions and the following disclaimer in the ! 37: * documentation and/or other materials provided with the distribution. ! 38: * 3. All advertising materials mentioning features or use of this software ! 39: * must display the following acknowledgement: ! 40: * This product includes software developed by the University of ! 41: * California, Berkeley and its contributors. ! 42: * 4. Neither the name of the University nor the names of its contributors ! 43: * may be used to endorse or promote products derived from this software ! 44: * without specific prior written permission. ! 45: * ! 46: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 47: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 48: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 49: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 50: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 51: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 52: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 53: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 54: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 55: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 56: * SUCH DAMAGE. ! 57: * ! 58: * @(#)vfs_cluster.c 8.10 (Berkeley) 3/28/95 ! 59: */ ! 60: ! 61: #include <sys/param.h> ! 62: #include <sys/proc.h> ! 63: #include <sys/buf.h> ! 64: #include <sys/vnode.h> ! 65: #include <sys/mount.h> ! 66: #include <sys/trace.h> ! 67: #include <sys/malloc.h> ! 68: #include <sys/resourcevar.h> ! 69: #include <libkern/libkern.h> ! 70: #include <kern/mapfs.h> ! 71: ! 72: #include <kern/kdebug.h> ! 73: ! 74: /* ! 75: * Local declarations ! 76: */ ! 77: struct buf *cluster_rbuild __P((struct vnode *, u_quad_t, struct buf *, ! 78: daddr_t, daddr_t, long, int, long, long)); ! 79: struct buf *cluster_create __P((struct vnode *, struct buf *, daddr_t, daddr_t, long, ! 80: int, long, daddr_t *)); ! 81: int cluster_block __P((struct vnode *, u_quad_t, struct buf *, long, long)); ! 82: void cluster_wbuild __P((struct vnode *, struct buf *, long, ! 83: daddr_t, int, daddr_t, long, int)); ! 84: struct cluster_save *cluster_collectbufs __P((struct vnode *, struct buf *)); ! 85: ! 86: #if DIAGNOSTIC ! 87: /* ! 88: * Set to 1 if reads of block zero should cause readahead to be done. ! 89: * Set to 0 treats a read of block zero as a non-sequential read. ! 90: * ! 91: * Setting to one assumes that most reads of block zero of files are due to ! 92: * sequential passes over the files (e.g. cat, sum) where additional blocks ! 93: * will soon be needed. Setting to zero assumes that the majority are ! 94: * surgical strikes to get particular info (e.g. size, file) where readahead ! 95: * blocks will not be used and, in fact, push out other potentially useful ! 96: * blocks from the cache. The former seems intuitive, but some quick tests ! 97: * showed that the latter performed better from a system-wide point of view. ! 98: */ ! 99: int doclusterraz = 0; ! 100: #define ISSEQREAD(vp, blk) \ ! 101: (((blk) != 0 || doclusterraz) && \ ! 102: ((blk) == (vp)->v_lastr + 1 || (blk) == (vp)->v_lastr)) ! 103: #else ! 104: #define ISSEQREAD(vp, blk) \ ! 105: ((blk) != 0 && ((blk) == (vp)->v_lastr + 1 || (blk) == (vp)->v_lastr)) ! 106: #endif ! 107: ! 108: /* ! 109: * This replaces bread. If this is a bread at the beginning of a file and ! 110: * lastr is 0, we assume this is the first read and we'll read up to two ! 111: * blocks if they are sequential. After that, we'll do regular read ahead ! 112: * in clustered chunks. ! 113: * ! 114: * There are 4 or 5 cases depending on how you count: ! 115: * Desired block is in the cache: ! 116: * 1 Not sequential access (0 I/Os). ! 117: * 2 Access is sequential, do read-ahead (1 ASYNC). ! 118: * Desired block is not in cache: ! 119: * 3 Not sequential access (1 SYNC). ! 120: * 4 Sequential access, next block is contiguous (1 SYNC). ! 121: * 5 Sequential access, next block is not contiguous (1 SYNC, 1 ASYNC) ! 122: * ! 123: * There are potentially two buffers that require I/O. ! 124: * bp is the block requested. ! 125: * rbp is the read-ahead block. ! 126: * If either is NULL, then you don't have to do the I/O. ! 127: */ ! 128: ! 129: cluster_read(vp, filesize, lblkno, size, cred, bpp, secsize, ! 130: firstpass, resid, fp_sequential) ! 131: struct vnode *vp; ! 132: u_quad_t filesize; ! 133: daddr_t lblkno; ! 134: long size; ! 135: struct ucred *cred; ! 136: struct buf **bpp; ! 137: long secsize; ! 138: int firstpass; ! 139: long resid; ! 140: int *fp_sequential; ! 141: { ! 142: struct buf *bp, *rbp, *cbp; ! 143: daddr_t blkno, ioblkno; ! 144: long flags; ! 145: int error, num_ra, alreadyincore; ! 146: long num; ! 147: int sequential, case4; ! 148: ! 149: #if DIAGNOSTIC ! 150: if (size == 0) ! 151: panic("cluster_read: size = 0"); ! 152: #endif ! 153: ! 154: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_START, ! 155: lblkno, ! 156: resid, ! 157: firstpass, ! 158: vp, ! 159: 0); ! 160: error = 0; ! 161: flags = B_READ; ! 162: *bpp = bp = getblk(vp, lblkno, size, 0, 0); ! 163: ! 164: if (resid == PAGE_SIZE && lblkno && !ISSEQREAD(vp, lblkno) && ! 165: (vp->v_mount->mnt_stat.f_iosize & (PAGE_SIZE - 1)) == 0) { ! 166: if (bp->b_flags & B_CACHE) { ! 167: ! 168: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_END, ! 169: lblkno, ! 170: size, ! 171: -1, ! 172: 0, ! 173: 0); ! 174: ! 175: vp->v_consumed += (bp->b_bcount/size); ! 176: return (0); ! 177: } ! 178: bp->b_flags |= B_READ; ! 179: ! 180: if (cluster_block(vp, filesize, bp, size, secsize)) { ! 181: ! 182: error = biowait(bp); ! 183: ! 184: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_END, ! 185: bp, ! 186: 0, ! 187: 0, ! 188: 0, ! 189: 0); ! 190: ! 191: return(error); ! 192: } ! 193: } ! 194: /* round up resid count to nearest block size */ ! 195: if ( resid > size ) ! 196: resid += size - 1; ! 197: ! 198: if (bp->b_flags & B_CACHE) { ! 199: /* ! 200: * Desired block is in cache; do any readahead ASYNC. ! 201: * Case 1, 2. ! 202: */ ! 203: trace(TR_BREADHIT, pack(vp, size), lblkno); ! 204: flags |= B_ASYNC; ! 205: if (resid > size) ! 206: resid -= size; ! 207: ! 208: if (!fp_sequential) ! 209: { ! 210: ioblkno = lblkno + 1; ! 211: alreadyincore = incore(vp, ioblkno) != NULL; ! 212: } ! 213: else ! 214: { ! 215: ioblkno = lblkno + (vp->v_ralen ? vp->v_ralen : 1); ! 216: alreadyincore = incore(vp, ioblkno) != NULL; ! 217: } ! 218: /* ! 219: * treat this as a hit for purposes of speculative I/O around paging activity ! 220: */ ! 221: vp->v_consumed += (bp->b_bcount/size); ! 222: ! 223: bp = NULL; ! 224: } else { ! 225: /* Block wasn't in cache, case 3, 4, 5. */ ! 226: trace(TR_BREADMISS, pack(vp, size), lblkno); ! 227: bp->b_flags |= B_READ; ! 228: ioblkno = lblkno; ! 229: alreadyincore = 0; ! 230: current_proc()->p_stats->p_ru.ru_inblock++; /* XXX */ ! 231: } ! 232: /* ! 233: * XXX ! 234: * Replace 1 with a window size based on some permutation of ! 235: * maxcontig and rot_delay. This will let you figure out how ! 236: * many blocks you should read-ahead (case 2, 4, 5). ! 237: * ! 238: * If the access isn't sequential, reset the window to 1. ! 239: * Note that a read to the same block is considered sequential. ! 240: * This catches the case where the file is being read sequentially, ! 241: * but at smaller than the filesystem block size. ! 242: */ ! 243: rbp = NULL; ! 244: cbp = NULL; ! 245: case4 = 0; ! 246: ! 247: if (!ISSEQREAD(vp, lblkno)) { ! 248: vp->v_ralen = 0; ! 249: vp->v_maxra = lblkno; ! 250: sequential = 0; ! 251: } ! 252: else ! 253: sequential = 1; ! 254: ! 255: /* On first pass set the sequential state. ! 256: * Otherwise, just use the value passed in. ! 257: */ ! 258: if (firstpass) ! 259: *fp_sequential = sequential; ! 260: ! 261: if (resid > size || *fp_sequential) { ! 262: if (((u_quad_t)(ioblkno + 1)) * (u_quad_t)size <= filesize && !alreadyincore && ! 263: !(error = VOP_BMAP(vp, ioblkno, NULL, &blkno, &num_ra)) && ! 264: blkno != -1) { ! 265: /* ! 266: * Reading sequentially, and the next block is not in the ! 267: * cache. We are going to try reading ahead. ! 268: */ ! 269: if (num_ra) { ! 270: /* ! 271: * If our desired readahead block had been read ! 272: * in a previous readahead but is no longer in ! 273: * core, then we may be reading ahead too far ! 274: * or are not using our readahead very rapidly. ! 275: * In this case we scale back the window. ! 276: */ ! 277: if (*fp_sequential) { ! 278: if (!alreadyincore && ioblkno <= vp->v_maxra) ! 279: vp->v_ralen = max(vp->v_ralen >> 1, 1); ! 280: /* ! 281: * There are more sequential blocks than our current ! 282: * window allows, scale up. Ideally we want to get ! 283: * in sync with the filesystem maxcontig value. ! 284: */ ! 285: else if (num_ra > vp->v_ralen && lblkno != vp->v_lastr) ! 286: vp->v_ralen = vp->v_ralen ? ! 287: min(num_ra, vp->v_ralen << 1) : 1; ! 288: } ! 289: num = max((resid/size)-1, vp->v_ralen); ! 290: num_ra = min(num, num_ra); ! 291: } ! 292: ! 293: if (num_ra) { /* case 2, 4 */ ! 294: cbp = cluster_rbuild(vp, filesize, ! 295: bp, ioblkno, blkno, size, num_ra, flags, secsize); ! 296: ! 297: if ( !(cbp->b_flags & B_CALL)) { ! 298: if ((rbp = cbp) == bp) ! 299: rbp = NULL; ! 300: cbp = NULL; ! 301: } else ! 302: case4 = 1; ! 303: } else if (ioblkno == lblkno) { ! 304: bp->b_blkno = blkno; ! 305: /* Case 5: check how many blocks to read ahead */ ! 306: ++ioblkno; ! 307: if (((u_quad_t)(ioblkno + 1)) * (u_quad_t)size > filesize || ! 308: incore(vp, ioblkno) || (error = VOP_BMAP(vp, ! 309: ioblkno, NULL, &blkno, &num_ra)) || blkno == -1) ! 310: goto skip_readahead; ! 311: /* ! 312: * Adjust readahead as above. ! 313: * Don't check alreadyincore, we know it is 0 from ! 314: * the previous conditional. ! 315: */ ! 316: if (num_ra) { ! 317: if (*fp_sequential) { ! 318: if (ioblkno <= vp->v_maxra) ! 319: vp->v_ralen = max(vp->v_ralen >> 1, 1); ! 320: else if (num_ra > vp->v_ralen && ! 321: lblkno != vp->v_lastr) ! 322: vp->v_ralen = vp->v_ralen ? ! 323: min(num_ra,vp->v_ralen<<1) : 1; ! 324: } ! 325: num = max((resid/size)-1, vp->v_ralen); ! 326: num_ra = min(num, num_ra); ! 327: } ! 328: flags |= B_ASYNC; ! 329: ! 330: if (num_ra) { ! 331: cbp = cluster_rbuild(vp, filesize, ! 332: NULL, ioblkno, blkno, size, num_ra, flags, ! 333: secsize); ! 334: if ( !(cbp->b_flags & B_CALL)) { ! 335: rbp = cbp; ! 336: cbp = NULL; ! 337: } ! 338: } else { ! 339: rbp = getblk(vp, ioblkno, size, 0, 0); ! 340: rbp->b_flags |= flags; ! 341: rbp->b_blkno = blkno; ! 342: } ! 343: } else { ! 344: /* case 2; read ahead single block */ ! 345: rbp = getblk(vp, ioblkno, size, 0, 0); ! 346: rbp->b_flags |= flags; ! 347: rbp->b_blkno = blkno; ! 348: } ! 349: if (cbp || rbp) { /* case 2, 5 */ ! 350: trace(TR_BREADMISSRA, ! 351: pack(vp, (num_ra + 1) * size), ioblkno); ! 352: current_proc()->p_stats->p_ru.ru_inblock++; /* XXX */ ! 353: } ! 354: } ! 355: } ! 356: ! 357: skip_readahead: ! 358: if (bp && !case4) { ! 359: if (bp->b_flags & (B_DONE | B_DELWRI)) ! 360: panic("cluster_read: DONE bp"); ! 361: else { ! 362: /* ! 363: * issue the BMAP here if needed due to the block device's ! 364: * lack of a BMAP call in the strategy routine.... when being ! 365: * used by the filesystem/mount code, the blockno's being worked ! 366: * with are always physical so the strategy routine doesn't bother. ! 367: * Now that we are calling cluster read/write from spec_read/spec_write ! 368: * we have to use real logical blockno's in order to properly trigger ! 369: * the read-ahead and write-coalescing. ! 370: */ ! 371: if (bp->b_lblkno == bp->b_blkno) { ! 372: VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL); ! 373: ! 374: if ((long)bp->b_blkno == -1) ! 375: clrbuf(bp); ! 376: } ! 377: error = VOP_STRATEGY(bp); ! 378: ! 379: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_NONE, ! 380: bp->b_lblkno, ! 381: bp->b_bufsize, ! 382: bp->b_bcount, ! 383: 1, 0 ); ! 384: } ! 385: } ! 386: if (rbp) { ! 387: if (error || rbp->b_flags & (B_DONE | B_DELWRI)) { ! 388: rbp->b_flags &= ~(B_ASYNC | B_READ); ! 389: brelse(rbp); ! 390: } else { ! 391: /* ! 392: * issue the BMAP here if needed due to the block device's ! 393: * lack of a BMAP call in the strategy routine.... when being ! 394: * used by the filesystem/mount code, the blockno's being worked ! 395: * with are always physical so the strategy routine doesn't bother. ! 396: * Now that we are calling cluster read/write from spec_read/spec_write ! 397: * we have to use real logical blockno's in order to properly trigger ! 398: * the read-ahead and write-coalescing. ! 399: */ ! 400: if (rbp->b_lblkno == rbp->b_blkno) { ! 401: VOP_BMAP(vp, rbp->b_lblkno, NULL, &rbp->b_blkno, NULL); ! 402: ! 403: if ((long)rbp->b_blkno == -1) ! 404: clrbuf(rbp); ! 405: } ! 406: (void) VOP_STRATEGY(rbp); ! 407: ! 408: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_NONE, ! 409: rbp->b_lblkno, ! 410: rbp->b_bufsize, ! 411: rbp->b_bcount, ! 412: 2, 0 ); ! 413: } ! 414: } ! 415: if (cbp) { ! 416: (void) VOP_STRATEGY(cbp); ! 417: ! 418: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_NONE, ! 419: cbp->b_lblkno, ! 420: cbp->b_bufsize, ! 421: cbp->b_bcount, ! 422: 3, 0 ); ! 423: } ! 424: /* ! 425: * Recalculate our maximum readahead ! 426: */ ! 427: if (rbp == NULL) { ! 428: if (cbp) ! 429: rbp = cbp; ! 430: else ! 431: rbp = bp; ! 432: } ! 433: if (rbp) ! 434: vp->v_maxra = rbp->b_lblkno + (rbp->b_bcount / size) - 1; ! 435: ! 436: if (bp) ! 437: error = biowait(bp); ! 438: ! 439: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 11)) | DBG_FUNC_END, ! 440: bp, ! 441: rbp, ! 442: cbp, ! 443: vp->v_maxra, ! 444: 0); ! 445: return(error); ! 446: } ! 447: ! 448: struct pent { ! 449: int mask; ! 450: int num; ! 451: } pent[7] = { ! 452: {0,0}, ! 453: {0,0}, ! 454: {~0,1}, ! 455: {~1,2}, ! 456: {~3,4}, ! 457: {~7,8}, ! 458: {~15,16}}; ! 459: ! 460: ! 461: int cluster_block(vp, filesize, bp, size, secsize) ! 462: struct vnode *vp; ! 463: u_quad_t filesize; ! 464: struct buf *bp; ! 465: long size; ! 466: long secsize; ! 467: { ! 468: struct buf *cbp; ! 469: daddr_t lblkno, blkno, ioblkno, lbn; ! 470: int num_io, num; ! 471: unsigned ratio; ! 472: ! 473: #if 0 /* FIXED READS */ ! 474: /* calculate maximum number of blocks to read in */ ! 475: ! 476: lblkno = bp->b_lblkno & ~0x07; /* put us on a 32k (8 page boundary) boundary */ ! 477: num = 8; ! 478: num_io = 0; ! 479: #else /* ADAPTIVE READS */ ! 480: if (vp->v_bread > vp->v_trigger) { ! 481: ratio = (vp->v_consumed*100) / vp->v_bread; ! 482: ! 483: if (ratio < 50 && vp->v_power > 2) { ! 484: vp->v_power--; ! 485: vp->v_trigger = vp->v_bread + (16 * pent[vp->v_power].num); ! 486: } else if (ratio > 75 && vp->v_power < 6) { ! 487: vp->v_power++; ! 488: vp->v_trigger = vp->v_bread + (16 * pent[vp->v_power].num); ! 489: } ! 490: } ! 491: if ((num = pent[vp->v_power].num) == 1) ! 492: return (0); ! 493: lblkno = bp->b_lblkno & pent[vp->v_power].mask; ! 494: num_io = 0; ! 495: #endif ! 496: ! 497: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 14)) | DBG_FUNC_START, ! 498: lblkno, ! 499: num, ! 500: vp->v_flag, ! 501: vp, ! 502: 0 ); ! 503: ! 504: for (lbn = bp->b_lblkno; lbn > lblkno; lbn--) { ! 505: if (incore(vp, lbn - 1)) ! 506: break; ! 507: } ! 508: num -= (lbn - lblkno); ! 509: ! 510: for (;;) { ! 511: if (VOP_BMAP(vp, lbn, NULL, &blkno, &num_io) || blkno == -1 || num_io == 0) { ! 512: if (lbn == bp->b_lblkno) { ! 513: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 14)) | DBG_FUNC_END, ! 514: -1, ! 515: lbn, ! 516: blkno, ! 517: num_io, ! 518: 0); ! 519: return (0); ! 520: } ! 521: } ! 522: if ((lbn + num_io) >= bp->b_lblkno) ! 523: break; ! 524: lbn++; ! 525: num--; ! 526: } ! 527: if ((num_io = min(num, num_io + 1)) == 1) ! 528: return (0); ! 529: ! 530: if ((u_quad_t)size * ((u_quad_t)(lbn + num_io)) > filesize) ! 531: num_io = (filesize - ((u_quad_t)size * (u_quad_t)lbn)) / size; ! 532: ! 533: cbp = cluster_create(vp, bp, lbn, blkno, size, num_io, secsize, &ioblkno); ! 534: ! 535: if (cbp) { ! 536: (void) VOP_STRATEGY(cbp); ! 537: vp->v_bread += (cbp->b_bcount / size); ! 538: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 14)) | DBG_FUNC_END, ! 539: cbp->b_lblkno, ! 540: cbp->b_blkno, ! 541: cbp->b_bcount, ! 542: 0, ! 543: 0 ); ! 544: ! 545: return (1); ! 546: } ! 547: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 14)) | DBG_FUNC_END, ! 548: 0, ! 549: 0, ! 550: 0, ! 551: 0, ! 552: 0); ! 553: return (0); ! 554: } ! 555: ! 556: ! 557: /* ! 558: * generate advisory I/O in as big of chunks as possible ! 559: * and then parcel them up into logical blocks in the buffer hash table. ! 560: */ ! 561: advisory_read(vp, filesize, lblkno, size, runt_size, io_size, secsize) ! 562: struct vnode *vp; ! 563: u_quad_t filesize; ! 564: daddr_t lblkno; ! 565: long size; ! 566: long runt_size; ! 567: long io_size; ! 568: long secsize; ! 569: { ! 570: struct buf *bp, *cbp; ! 571: daddr_t blkno, ioblkno; ! 572: int error, num_io; ! 573: long num; ! 574: ! 575: error = 0; ! 576: ! 577: /* calculate maximum number of blocks to read in */ ! 578: ! 579: num = (io_size + (size - 1)) / size; ! 580: ! 581: if ((u_quad_t)size * ((u_quad_t)(lblkno + num)) > filesize) { ! 582: if (((u_quad_t)size * (u_quad_t)lblkno) >= filesize) ! 583: return(EFBIG); ! 584: io_size = filesize - ((u_quad_t)size * (u_quad_t)lblkno); ! 585: ! 586: num = io_size / size; ! 587: } else ! 588: io_size = num * size; ! 589: ! 590: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 13)) | DBG_FUNC_START, ! 591: lblkno, ! 592: io_size, ! 593: num, ! 594: vp, ! 595: 0 ); ! 596: ! 597: while (num) { ! 598: if (error = VOP_BMAP(vp, lblkno, NULL, &blkno, &num_io)) ! 599: break; ! 600: ! 601: if (blkno == -1) { ! 602: lblkno++; ! 603: num--; ! 604: io_size -= size; ! 605: continue; ! 606: } ! 607: num_io = min(num, num_io + 1); ! 608: ! 609: cbp = cluster_create(vp, NULL, lblkno, blkno, size, num_io, secsize, &ioblkno); ! 610: ! 611: if (cbp) { ! 612: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 13)) | DBG_FUNC_NONE, ! 613: cbp->b_lblkno, ! 614: cbp->b_blkno, ! 615: cbp->b_bcount, ! 616: 3, ! 617: 0 ); ! 618: ! 619: (void) VOP_STRATEGY(cbp); ! 620: } else { ! 621: if (ioblkno == lblkno) { ! 622: error = ENOMEM; ! 623: break; ! 624: } ! 625: } ! 626: io_size -= ((ioblkno - lblkno) * size); ! 627: num -= ioblkno - lblkno; ! 628: lblkno = ioblkno; ! 629: } ! 630: if (io_size && !error) { ! 631: bp = getblk(vp, lblkno, runt_size, 0, 0); ! 632: ! 633: if (bp->b_flags & (B_DONE | B_DELWRI)) ! 634: brelse(bp); ! 635: else { ! 636: bp->b_flags |= (B_READ | B_ASYNC); ! 637: ! 638: (void) VOP_STRATEGY(bp); ! 639: ! 640: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 13)) | DBG_FUNC_NONE, ! 641: bp->b_lblkno, ! 642: bp->b_blkno, ! 643: bp->b_bcount, ! 644: 4, ! 645: 0 ); ! 646: } ! 647: io_size -= runt_size; ! 648: } ! 649: ! 650: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 13)) | DBG_FUNC_END, ! 651: lblkno, ! 652: io_size, ! 653: num, ! 654: error, ! 655: 0); ! 656: return(error); ! 657: } ! 658: ! 659: ! 660: /* ! 661: * If blocks are contiguous on disk, use this to provide clustered ! 662: * read ahead. We will read as many blocks as possible sequentially ! 663: * and then parcel them up into logical blocks in the buffer hash table. ! 664: */ ! 665: struct buf * ! 666: cluster_rbuild(vp, filesize, bp, lbn, blkno, size, run, flags, secsize) ! 667: struct vnode *vp; ! 668: u_quad_t filesize; ! 669: struct buf *bp; ! 670: daddr_t lbn; ! 671: daddr_t blkno; ! 672: long size; ! 673: int run; ! 674: long flags; ! 675: long secsize; ! 676: { ! 677: struct cluster_save *b_save; ! 678: struct buf *tbp, *cbp; ! 679: caddr_t cp; ! 680: daddr_t bn; ! 681: int i, inc; ! 682: ! 683: #if DIAGNOSTIC ! 684: if (size != vp->v_mount->mnt_stat.f_iosize) ! 685: panic("cluster_rbuild: size %d != filesize %d\n", ! 686: size, vp->v_mount->mnt_stat.f_iosize); ! 687: #endif ! 688: if ((u_quad_t)size * ((u_quad_t)(lbn + run + 1)) > filesize) ! 689: --run; ! 690: if (run == 0) { ! 691: if (!bp) { ! 692: bp = getblk(vp, lbn, size, 0, 0); ! 693: bp->b_blkno = blkno; ! 694: bp->b_flags |= flags; ! 695: } ! 696: return(bp); ! 697: } ! 698: b_save = _MALLOC(sizeof(struct buf *) * (run + 1) + sizeof(struct cluster_save), ! 699: M_SEGMENT, M_NOWAIT); ! 700: ! 701: if (b_save) ! 702: cbp = alloc_io_buf(vp); ! 703: else ! 704: cbp = NULL; ! 705: ! 706: if (b_save == NULL || cbp == NULL) { ! 707: if (b_save) ! 708: _FREE(b_save, M_SEGMENT); ! 709: if (cbp) ! 710: free_io_buf(cbp); ! 711: return (bp); ! 712: } ! 713: b_save->bs_bufsize = size; ! 714: b_save->bs_nchildren = 0; ! 715: b_save->bs_children = (struct buf **)(b_save + 1); ! 716: ! 717: cbp->b_saveaddr = (caddr_t)b_save; ! 718: cbp->b_iodone = cluster_callback; ! 719: cbp->b_blkno = blkno; ! 720: cbp->b_lblkno = lbn; ! 721: cbp->b_flags |= flags | B_CALL; ! 722: ! 723: inc = btodb(size, secsize); ! 724: cp = (char *)cbp->b_data; ! 725: tbp = bp; ! 726: ! 727: for (bn = blkno, i = 0; i <= run; ++i, bn += inc) { ! 728: if (tbp == NULL) { ! 729: if (incore(vp, lbn + i)) ! 730: /* ! 731: * A component of the cluster is already in core, ! 732: * terminate the cluster early. ! 733: */ ! 734: break; ! 735: tbp = getblk(vp, lbn + i, size, 0, 0); ! 736: } ! 737: pagemove(tbp->b_data, cp, size); ! 738: cbp->b_bcount += size; ! 739: cbp->b_bufsize += size; ! 740: cp += size; ! 741: ! 742: if (bp != tbp) ! 743: tbp->b_flags |= flags | B_READ | B_ASYNC; ! 744: tbp->b_bufsize -= size; ! 745: tbp->b_blkno = bn; ! 746: ! 747: b_save->bs_children[i] = tbp; ! 748: b_save->bs_nchildren++; ! 749: ! 750: tbp = NULL; ! 751: } ! 752: /* ! 753: * The cluster may have been terminated early ! 754: * If no cluster could be formed, deallocate the cluster save info. ! 755: */ ! 756: if (i == 0) { ! 757: _FREE(b_save, M_SEGMENT); ! 758: free_io_buf(cbp); ! 759: return(bp); ! 760: } ! 761: return(cbp); ! 762: } ! 763: ! 764: ! 765: ! 766: struct buf * ! 767: cluster_create(vp, bp, lbn, blkno, size, run, secsize, ioblkno) ! 768: struct vnode *vp; ! 769: struct buf *bp; ! 770: daddr_t lbn; ! 771: daddr_t blkno; ! 772: long size; ! 773: int run; ! 774: long secsize; ! 775: daddr_t *ioblkno; ! 776: { ! 777: struct cluster_save *b_save; ! 778: struct buf *tbp, *cbp; ! 779: caddr_t cp; ! 780: daddr_t bn; ! 781: int i, inc; ! 782: ! 783: inc = btodb(size, secsize); ! 784: ! 785: if (bp == NULL) { ! 786: while (run && (tbp = incore(vp, lbn))) { ! 787: /* ! 788: * if a block is already in core ! 789: * and is not busy ! 790: * then get and release to freshen it in the LRU ! 791: */ ! 792: if ( !(tbp->b_flags & B_BUSY)) { ! 793: tbp = getblk(vp, lbn, size, 0, 0); ! 794: brelse(tbp); ! 795: } ! 796: lbn++; ! 797: run--; ! 798: blkno += inc; ! 799: } ! 800: if (run == 0) { ! 801: *ioblkno = lbn; ! 802: return (NULL); ! 803: } ! 804: } ! 805: b_save = _MALLOC((sizeof(struct buf *) * run) + sizeof(struct cluster_save), M_SEGMENT, M_NOWAIT); ! 806: ! 807: if (b_save) ! 808: cbp = alloc_io_buf(vp); ! 809: else ! 810: cbp = NULL; ! 811: ! 812: if (b_save == NULL || cbp == NULL) { ! 813: if (b_save) ! 814: _FREE(b_save, M_SEGMENT); ! 815: if (cbp) ! 816: free_io_buf(cbp); ! 817: *ioblkno = lbn; ! 818: ! 819: return (NULL); ! 820: } ! 821: b_save->bs_bufsize = size; ! 822: b_save->bs_nchildren = 0; ! 823: b_save->bs_children = (struct buf **)(b_save + 1); ! 824: ! 825: cbp->b_saveaddr = (caddr_t)b_save; ! 826: cbp->b_iodone = cluster_callback; ! 827: cbp->b_blkno = blkno; ! 828: cbp->b_lblkno = lbn; ! 829: cbp->b_flags |= (B_READ | B_ASYNC | B_CALL); ! 830: ! 831: cp = (char *)cbp->b_data; ! 832: ! 833: for (bn = blkno, i = 0; i < run; ++i, bn += inc, ++lbn) { ! 834: if (bp && bp->b_lblkno == lbn) ! 835: tbp = bp; ! 836: else { ! 837: if (tbp = incore(vp, lbn)) { ! 838: /* ! 839: * A component of the cluster is already in core, ! 840: * terminate the cluster early. ! 841: * if its not busy then also ! 842: * get and release to freshen it in the LRU ! 843: */ ! 844: if ( !(tbp->b_flags & B_BUSY)) { ! 845: tbp = getblk(vp, lbn, size, 0, 0); ! 846: brelse(tbp); ! 847: } ! 848: break; ! 849: } ! 850: tbp = getblk(vp, lbn, size, 0, 0); ! 851: } ! 852: pagemove(tbp->b_data, cp, size); ! 853: ! 854: tbp->b_bufsize -= size; ! 855: tbp->b_blkno = bn; ! 856: cbp->b_bcount += size; ! 857: cbp->b_bufsize += size; ! 858: cp += size; ! 859: ! 860: if (tbp != bp) ! 861: tbp->b_flags |= (B_READ | B_ASYNC); ! 862: b_save->bs_children[i] = tbp; ! 863: b_save->bs_nchildren++; ! 864: } ! 865: *ioblkno = lbn; ! 866: /* ! 867: * The cluster may have been terminated early ! 868: * If no cluster could be formed, deallocate the cluster save info. ! 869: */ ! 870: if (cbp->b_bcount == 0) { ! 871: _FREE(b_save, M_SEGMENT); ! 872: free_io_buf(cbp); ! 873: return(NULL); ! 874: } ! 875: return(cbp); ! 876: } ! 877: ! 878: ! 879: /* ! 880: * Cleanup after a clustered read or write. ! 881: * This is complicated by the fact that any of the buffers might have ! 882: * extra memory (if there were no empty buffer headers at allocbuf time) ! 883: * that we will need to shift around. ! 884: */ ! 885: void ! 886: cluster_callback(bp) ! 887: struct buf *bp; ! 888: { ! 889: struct cluster_save *b_save; ! 890: struct buf **bpp, *tbp; ! 891: long bsize; ! 892: int xsize; ! 893: int n; ! 894: caddr_t cp; ! 895: int error = 0; ! 896: ! 897: /* ! 898: * Must propogate errors to all the components. ! 899: */ ! 900: if (bp->b_flags & B_ERROR) ! 901: error = bp->b_error; ! 902: b_save = (struct cluster_save *)(bp->b_saveaddr); ! 903: ! 904: bsize = b_save->bs_bufsize; ! 905: xsize = bp->b_bcount - bp->b_resid; ! 906: cp = (char *)bp->b_data; ! 907: /* ! 908: * Move memory from the large cluster buffer into the component ! 909: * buffers and mark IO as done on these. ! 910: */ ! 911: for (bpp = b_save->bs_children; b_save->bs_nchildren--; ++bpp) { ! 912: tbp = *bpp; ! 913: pagemove(cp, tbp->b_data, bsize); ! 914: tbp->b_bufsize += bsize; ! 915: ! 916: n = min(bsize, xsize); ! 917: xsize -= n; ! 918: ! 919: if ((tbp->b_bcount = n) == 0) ! 920: tbp->b_flags |= B_INVAL; ! 921: tbp->b_resid = bsize - n; ! 922: ! 923: if (error) { ! 924: tbp->b_flags |= B_ERROR; ! 925: tbp->b_error = error; ! 926: } ! 927: biodone(tbp); ! 928: bp->b_bufsize -= bsize; ! 929: cp += bsize; ! 930: } ! 931: _FREE(b_save, M_SEGMENT); ! 932: ! 933: free_io_buf(bp); ! 934: } ! 935: ! 936: ! 937: /* ! 938: * on close, flush out any remaining cluster ! 939: * ! 940: */ ! 941: cluster_close(vp, bsize, secsize) ! 942: struct vnode *vp; ! 943: int bsize; ! 944: long secsize; ! 945: { ! 946: int cursize; ! 947: ! 948: if (vp->v_clen) { ! 949: cursize = vp->v_lastw - vp->v_cstart + 1; ! 950: ! 951: cluster_wbuild(vp, NULL, bsize, vp->v_cstart, cursize, -1, secsize, 0); ! 952: ! 953: vp->v_lasta = vp->v_clen = vp->v_cstart = vp->v_lastw = 0; ! 954: } ! 955: } ! 956: ! 957: ! 958: /* ! 959: * Do clustered write for FFS. ! 960: * ! 961: * Three cases: ! 962: * 1. Write is not sequential (write asynchronously) ! 963: * Write is sequential: ! 964: * 2. beginning of cluster - begin cluster ! 965: * 3. middle of a cluster - add to cluster ! 966: * 4. end of a cluster - asynchronously write cluster ! 967: */ ! 968: ! 969: cluster_write(bp, filesize, secsize) ! 970: struct buf *bp; ! 971: u_quad_t filesize; ! 972: long secsize; ! 973: { ! 974: struct vnode *vp; ! 975: daddr_t lbn; ! 976: daddr_t bn; ! 977: int maxclen, cursize; ! 978: int need_commit; ! 979: int need_sync; ! 980: int bsize; ! 981: int error = 0; ! 982: ! 983: need_commit = (bp->b_flags & B_CLUST_COMMIT); ! 984: need_sync = (bp->b_flags & B_CLUST_SYNC); ! 985: bp->b_flags &= ~(B_CLUST_COMMIT | B_CLUST_SYNC); ! 986: ! 987: vp = bp->b_vp; ! 988: bn = bp->b_blkno; ! 989: lbn = bp->b_lblkno; ! 990: bsize = bp->b_bcount; ! 991: ! 992: if ((bsize & (PAGE_SIZE - 1)) || bsize > MAXBSIZE) { ! 993: bp->b_flags |= B_AGE; ! 994: bawrite(bp); ! 995: ! 996: return (error); ! 997: } ! 998: /* Initialize vnode to beginning of file. */ ! 999: if (lbn == 0) ! 1000: vp->v_lasta = vp->v_clen = vp->v_cstart = vp->v_lastw = 0; ! 1001: ! 1002: ! 1003: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_START, ! 1004: bp->b_lblkno, ! 1005: bp->b_bcount, ! 1006: vp->v_lasta, ! 1007: vp->v_clen, ! 1008: 0); ! 1009: ! 1010: if (vp->v_clen == 0 || lbn != vp->v_lastw + 1 || ! 1011: (bn != vp->v_lasta + btodb(bsize, secsize))) ! 1012: { ! 1013: maxclen = (MAXPHYSIO / bsize) - 1; ! 1014: ! 1015: if (vp->v_clen != 0) { ! 1016: /* ! 1017: * Next block is not sequential. ! 1018: * ! 1019: * If we are not writing at end of file, the process ! 1020: * seeked to another point in the file since its ! 1021: * last write, or we have reached our maximum ! 1022: * cluster size, then push the previous cluster. ! 1023: * Otherwise try reallocating to make it sequential. ! 1024: */ ! 1025: cursize = vp->v_lastw - vp->v_cstart + 1; ! 1026: if (((u_quad_t)(lbn + 1)) * (u_quad_t)bsize != filesize || ! 1027: lbn != vp->v_lastw + 1 || vp->v_clen <= cursize) { ! 1028: cluster_wbuild(vp, NULL, bsize, ! 1029: vp->v_cstart, cursize, lbn, secsize, need_sync); ! 1030: } else { ! 1031: struct buf **bpp, **endbp; ! 1032: struct cluster_save *buflist; ! 1033: ! 1034: buflist = cluster_collectbufs(vp, bp); ! 1035: ! 1036: if (buflist == NULL) { ! 1037: cluster_wbuild(vp, NULL, bp->b_bcount, ! 1038: vp->v_cstart, cursize, lbn, secsize, need_sync); ! 1039: } else { ! 1040: ! 1041: endbp = &buflist->bs_children ! 1042: [buflist->bs_nchildren - 1]; ! 1043: if (VOP_REALLOCBLKS(vp, buflist)) { ! 1044: /* ! 1045: * Failed, push the previous cluster. ! 1046: */ ! 1047: for (bpp = buflist->bs_children; ! 1048: bpp < endbp; bpp++) ! 1049: brelse(*bpp); ! 1050: _FREE(buflist, M_SEGMENT); ! 1051: ! 1052: cluster_wbuild(vp, NULL, bsize, ! 1053: vp->v_cstart, cursize, lbn, secsize, need_sync); ! 1054: } else { ! 1055: /* ! 1056: * Succeeded, keep building cluster. ! 1057: * don't bdwrite the last bp, we'll ! 1058: * first check to see if we now have a full ! 1059: * cluster, or the caller has requested a SYNC write ! 1060: */ ! 1061: for (bpp = buflist->bs_children; ! 1062: bpp < endbp; bpp++) ! 1063: bdwrite(*bpp); ! 1064: _FREE(buflist, M_SEGMENT); ! 1065: /* ! 1066: * update the physical block number because, ! 1067: * VOP_REALLOCBLKS will have changed it ! 1068: */ ! 1069: bn = bp->b_blkno; ! 1070: goto chk_cluster_full; ! 1071: } ! 1072: } ! 1073: } ! 1074: } ! 1075: /* ! 1076: * Consider beginning a cluster. ! 1077: * If at end of file, make cluster as large as possible, ! 1078: * otherwise find size of existing cluster. ! 1079: */ ! 1080: if (((u_quad_t)(lbn + 1)) * (u_quad_t)bsize != filesize && ! 1081: (VOP_BMAP(vp, lbn, NULL, &bp->b_blkno, &maxclen) || ! 1082: bp->b_blkno == -1)) { ! 1083: bn = bp->b_blkno; ! 1084: vp->v_clen = 0; ! 1085: vp->v_cstart = lbn + 1; ! 1086: ! 1087: if (need_sync) ! 1088: bwrite(bp); ! 1089: else ! 1090: bawrite(bp); ! 1091: ! 1092: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END, ! 1093: bp->b_lblkno, ! 1094: bp->b_blkno, ! 1095: bp->b_bcount, ! 1096: vp->v_cstart, ! 1097: 1 ); ! 1098: goto check_for_commit; ! 1099: } ! 1100: bn = bp->b_blkno; ! 1101: ! 1102: if ((vp->v_clen = maxclen) == 0 || need_commit) { /* I/O not contiguous or we're being asked to do IO_SYNC and this is the last */ ! 1103: vp->v_cstart = lbn + 1; /* chunk of the I/O request */ ! 1104: ! 1105: if (need_sync) ! 1106: bwrite(bp); ! 1107: else ! 1108: bawrite(bp); ! 1109: ! 1110: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END, ! 1111: bp->b_lblkno, ! 1112: bp->b_blkno, ! 1113: bp->b_bcount, ! 1114: vp->v_cstart, ! 1115: 2 ); ! 1116: } else { /* Wait for rest of cluster */ ! 1117: vp->v_cstart = lbn; ! 1118: ! 1119: bdwrite(bp); ! 1120: ! 1121: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END, ! 1122: bp->b_lblkno, ! 1123: bp->b_blkno, ! 1124: bp->b_bcount, ! 1125: vp->v_cstart, ! 1126: 3 ); ! 1127: } ! 1128: goto check_for_commit; ! 1129: } ! 1130: chk_cluster_full: ! 1131: if ((lbn == vp->v_cstart + vp->v_clen) || need_commit) { ! 1132: /* ! 1133: * At end of cluster, write it out. ! 1134: */ ! 1135: cluster_wbuild(vp, bp, bsize, vp->v_cstart, ! 1136: (lbn - vp->v_cstart) + 1, lbn, secsize, need_sync); ! 1137: ! 1138: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END, ! 1139: vp->v_cstart, ! 1140: vp->v_clen + 1, ! 1141: lbn, ! 1142: 0, ! 1143: 4 ); ! 1144: vp->v_clen = 0; ! 1145: vp->v_cstart = lbn + 1; ! 1146: } else { ! 1147: /* ! 1148: * In the middle of a cluster, so just delay the ! 1149: * I/O for now. ! 1150: */ ! 1151: bdwrite(bp); ! 1152: ! 1153: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END, ! 1154: bp->b_lblkno, ! 1155: bp->b_blkno, ! 1156: bp->b_bcount, ! 1157: vp->v_cstart, ! 1158: 5 ); ! 1159: } ! 1160: check_for_commit: ! 1161: vp->v_lastw = lbn; ! 1162: vp->v_lasta = bn; ! 1163: ! 1164: if (need_commit) { ! 1165: bp = getblk(vp, lbn, bsize, 0, 0); ! 1166: ! 1167: if (bp->b_flags & B_ERROR) ! 1168: error = (bp->b_error ? bp->b_error : EIO); ! 1169: brelse(bp); ! 1170: } ! 1171: return (error); ! 1172: } ! 1173: ! 1174: ! 1175: /* ! 1176: * This is an awful lot like cluster_rbuild...wish they could be combined. ! 1177: * The last lbn argument is the current block on which I/O is being ! 1178: * performed. Check to see that it doesn't fall in the middle of ! 1179: * the current block (if last_bp == NULL). ! 1180: */ ! 1181: void ! 1182: cluster_wbuild(vp, last_bp, size, start_lbn, len, lbn, secsize, need_sync) ! 1183: struct vnode *vp; ! 1184: struct buf *last_bp; ! 1185: long size; ! 1186: daddr_t start_lbn; ! 1187: int len; ! 1188: daddr_t lbn; ! 1189: long secsize; ! 1190: int need_sync; ! 1191: { ! 1192: struct cluster_save *b_save; ! 1193: struct buf *bp, *tbp; ! 1194: caddr_t cp; ! 1195: int i, s; ! 1196: ! 1197: #if DIAGNOSTIC ! 1198: if (size != vp->v_mount->mnt_stat.f_iosize) ! 1199: panic("cluster_wbuild: size %d != filesize %d\n", ! 1200: size, vp->v_mount->mnt_stat.f_iosize); ! 1201: #endif ! 1202: redo: ! 1203: while ((!incore(vp, start_lbn) || start_lbn == lbn) && len) { ! 1204: ++start_lbn; ! 1205: --len; ! 1206: } ! 1207: /* Get more memory for current buffer */ ! 1208: if (len <= 1) { ! 1209: if (last_bp) { ! 1210: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1211: last_bp->b_lblkno, ! 1212: last_bp->b_blkno, ! 1213: last_bp->b_bcount, ! 1214: 1, ! 1215: 0 ); ! 1216: if (need_sync) ! 1217: bwrite(last_bp); ! 1218: else ! 1219: bawrite(last_bp); ! 1220: } else if (len) { ! 1221: bp = getblk(vp, start_lbn, size, 0, 0); ! 1222: ! 1223: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1224: bp->b_lblkno, ! 1225: bp->b_blkno, ! 1226: bp->b_bcount, ! 1227: 2, ! 1228: 0 ); ! 1229: if (bp->b_flags & B_DELWRI) { ! 1230: if (need_sync) ! 1231: bwrite(bp); ! 1232: else ! 1233: bawrite(bp); ! 1234: } else ! 1235: brelse(bp); ! 1236: } ! 1237: return; ! 1238: } ! 1239: b_save = _MALLOC(sizeof(struct buf *) * len + sizeof(struct cluster_save), ! 1240: M_SEGMENT, M_NOWAIT); ! 1241: if (b_save) ! 1242: bp = alloc_io_buf(vp); ! 1243: else ! 1244: bp = NULL; ! 1245: ! 1246: if (b_save == NULL || bp == NULL) { ! 1247: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1248: bp, ! 1249: b_save, ! 1250: 0, ! 1251: 4, ! 1252: 0 ); ! 1253: if (bp) ! 1254: free_io_buf(bp); ! 1255: if (b_save) ! 1256: _FREE(b_save, M_SEGMENT); ! 1257: ! 1258: for (i = 0; i < len; ++i, ++start_lbn) { ! 1259: if (!incore(vp, start_lbn)) ! 1260: continue; ! 1261: if (last_bp == NULL || start_lbn != lbn) { ! 1262: tbp = getblk(vp, start_lbn, size, 0, 0); ! 1263: ! 1264: if (tbp->b_flags & B_DELWRI) { ! 1265: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1266: tbp->b_lblkno, ! 1267: tbp->b_blkno, ! 1268: tbp->b_bcount, ! 1269: 5, ! 1270: 0 ); ! 1271: ! 1272: if (need_sync) ! 1273: bwrite(tbp); ! 1274: else ! 1275: bawrite(tbp); ! 1276: } else ! 1277: brelse(tbp); ! 1278: } ! 1279: } ! 1280: if (last_bp) { ! 1281: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1282: last_bp->b_lblkno, ! 1283: last_bp->b_blkno, ! 1284: last_bp->b_bcount, ! 1285: 6, ! 1286: 0 ); ! 1287: if (need_sync) ! 1288: bwrite(last_bp); ! 1289: else ! 1290: bawrite(last_bp); ! 1291: } ! 1292: return; ! 1293: } ! 1294: b_save->bs_bufsize = size; ! 1295: b_save->bs_nchildren = 0; ! 1296: b_save->bs_children = (struct buf **)(b_save + 1); ! 1297: ! 1298: bp->b_saveaddr = (caddr_t)b_save; ! 1299: bp->b_iodone = cluster_callback; ! 1300: bp->b_flags |= (B_WRITEINPROG | B_CALL | B_ASYNC); ! 1301: ! 1302: cp = (char *)bp->b_data; ! 1303: ! 1304: for (start_lbn, i = 0; i < len; ++i, ++start_lbn) { ! 1305: /* ! 1306: * Block is not in core or the non-sequential block ! 1307: * ending our cluster was part of the cluster (in which ! 1308: * case we don't want to write it twice). ! 1309: */ ! 1310: if (!incore(vp, start_lbn) || ! 1311: (last_bp == NULL && start_lbn == lbn)) ! 1312: break; ! 1313: ! 1314: /* ! 1315: * Get the desired block buffer (unless it is the final ! 1316: * sequential block whose buffer was passed in explictly ! 1317: * as last_bp). ! 1318: */ ! 1319: if (last_bp == NULL || start_lbn != lbn) { ! 1320: tbp = getblk(vp, start_lbn, size, 0, 0); ! 1321: if (!(tbp->b_flags & B_DELWRI)) { ! 1322: brelse(tbp); ! 1323: break; ! 1324: } ! 1325: } else ! 1326: tbp = last_bp; ! 1327: ! 1328: if (i == 0) { ! 1329: bp->b_blkno = tbp->b_blkno; ! 1330: bp->b_lblkno= tbp->b_lblkno; ! 1331: } else { ! 1332: if (tbp->b_blkno != (bp->b_blkno + btodb(bp->b_bufsize, secsize))) { ! 1333: brelse(tbp); ! 1334: break; ! 1335: } ! 1336: } ! 1337: /* Move memory from children to parent */ ! 1338: pagemove(tbp->b_data, cp, size); ! 1339: bp->b_bcount += size; ! 1340: bp->b_bufsize += size; ! 1341: cp += size; ! 1342: ! 1343: tbp->b_bufsize -= size; ! 1344: tbp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI); ! 1345: tbp->b_flags |= (B_ASYNC | B_AGE); ! 1346: ! 1347: s = splbio(); ! 1348: reassignbuf(tbp, tbp->b_vp); /* put on clean list */ ! 1349: ++tbp->b_vp->v_numoutput; ! 1350: splx(s); ! 1351: ! 1352: b_save->bs_children[i] = tbp; ! 1353: b_save->bs_nchildren++; ! 1354: } ! 1355: ! 1356: KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_NONE, ! 1357: bp->b_lblkno, ! 1358: bp->b_blkno, ! 1359: bp->b_bcount, ! 1360: 7, ! 1361: 0 ); ! 1362: if (i == 0) { ! 1363: /* None to cluster */ ! 1364: free_io_buf(bp); ! 1365: _FREE(b_save, M_SEGMENT); ! 1366: } else { ! 1367: if (bp->b_bcount > MAXPHYSIO) ! 1368: panic("cluster_wbuild: bp->b_bcount = %x\n", bp->b_bcount); ! 1369: ! 1370: VOP_STRATEGY(bp); ! 1371: } ! 1372: if (i < len) { ! 1373: len -= i + 1; ! 1374: start_lbn += 1; ! 1375: goto redo; ! 1376: } ! 1377: } ! 1378: ! 1379: /* ! 1380: * Collect together all the buffers in a cluster. ! 1381: * Plus add one additional buffer. ! 1382: */ ! 1383: struct cluster_save * ! 1384: cluster_collectbufs(vp, last_bp) ! 1385: struct vnode *vp; ! 1386: struct buf *last_bp; ! 1387: { ! 1388: struct cluster_save *buflist; ! 1389: daddr_t lbn; ! 1390: int i, j, len; ! 1391: ! 1392: len = vp->v_lastw - vp->v_cstart + 1; ! 1393: buflist = _MALLOC(sizeof(struct buf *) * (len + 1) + sizeof(*buflist), ! 1394: M_SEGMENT, M_NOWAIT); ! 1395: ! 1396: if (buflist == NULL) ! 1397: return (NULL); ! 1398: ! 1399: buflist->bs_nchildren = 0; ! 1400: buflist->bs_children = (struct buf **)(buflist + 1); ! 1401: for (lbn = vp->v_cstart, i = 0; i < len; lbn++, i++) { ! 1402: (void)bread(vp, lbn, last_bp->b_bcount, NOCRED, ! 1403: &buflist->bs_children[i]); ! 1404: if(!(buflist->bs_children[i]->b_flags & B_DELWRI)) { ! 1405: for (j=0; j<=i; j++) ! 1406: brelse(buflist->bs_children[j]); ! 1407: _FREE(buflist, M_SEGMENT); ! 1408: return(NULL); ! 1409: } ! 1410: } ! 1411: buflist->bs_children[i] = last_bp; ! 1412: buflist->bs_nchildren = i + 1; ! 1413: return (buflist); ! 1414: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.