1 /*-
2 * Copyright (c) 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, 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 * @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95
34 * $FreeBSD$
35 */
36
37 #define BLKSIZE(a, b, c) blksize(a, b, c)
38 #define FS struct fs
39 #define I_FS i_fs
40 #define READ ffs_read
41 #define READ_S "ffs_read"
42 #define WRITE ffs_write
43 #define WRITE_S "ffs_write"
44
45 #include <vm/vm.h>
46 #include <vm/vm_object.h>
47 #include <vm/vm_pager.h>
48 #include <vm/vm_map.h>
49 #include <vm/vnode_pager.h>
50 #include <sys/poll.h>
51
52 /*
53 * Vnode op for reading.
54 */
55 /* ARGSUSED */
56 int
57 READ(ap)
58 struct vop_read_args /* {
59 struct vnode *a_vp;
60 struct uio *a_uio;
61 int a_ioflag;
62 struct ucred *a_cred;
63 } */ *ap;
64 {
65 register struct vnode *vp;
66 register struct inode *ip;
67 register struct uio *uio;
68 register FS *fs;
69 struct buf *bp;
70 ufs_daddr_t lbn, nextlbn;
71 off_t bytesinfile;
72 long size, xfersize, blkoffset;
73 int error, orig_resid;
74 u_short mode;
75 int seqcount;
76 int ioflag;
77 vm_object_t object;
78
79 vp = ap->a_vp;
80 seqcount = ap->a_ioflag >> 16;
81 ip = VTOI(vp);
82 mode = ip->i_mode;
83 uio = ap->a_uio;
84 ioflag = ap->a_ioflag;
85
86 #ifdef DIAGNOSTIC
87 if (uio->uio_rw != UIO_READ)
88 panic("%s: mode", READ_S);
89
90 if (vp->v_type == VLNK) {
91 if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
92 panic("%s: short symlink", READ_S);
93 } else if (vp->v_type != VREG && vp->v_type != VDIR)
94 panic("%s: type %d", READ_S, vp->v_type);
95 #endif
96 fs = ip->I_FS;
97 if ((u_int64_t)uio->uio_offset > fs->fs_maxfilesize)
98 return (EFBIG);
99
100 orig_resid = uio->uio_resid;
101 if (orig_resid <= 0)
102 return (0);
103
104 object = vp->v_object;
105
106 bytesinfile = ip->i_size - uio->uio_offset;
107 if (bytesinfile <= 0) {
108 if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
109 ip->i_flag |= IN_ACCESS;
110 return 0;
111 }
112
113 if (object)
114 vm_object_reference(object);
115 #if 1
116 /*
117 * If IO optimisation is turned on,
118 * and we are NOT a VM based IO request,
119 * (i.e. not headed for the buffer cache)
120 * but there IS a vm object associated with it.
121 */
122 if ((ioflag & IO_VMIO) == 0 && (vfs_ioopt > 1) && object) {
123 int nread, toread;
124
125 toread = uio->uio_resid;
126 if (toread > bytesinfile)
127 toread = bytesinfile;
128 if (toread >= PAGE_SIZE) {
129 /*
130 * Then if it's at least a page in size, try
131 * get the data from the object using vm tricks
132 */
133 error = uioread(toread, uio, object, &nread);
134 if ((uio->uio_resid == 0) || (error != 0)) {
135 /*
136 * If we finished or there was an error
137 * then finish up.
138 */
139 if ((error == 0 ||
140 uio->uio_resid != orig_resid) &&
141 (vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
142 ip->i_flag |= IN_ACCESS;
143 if (object)
144 /*
145 * This I don't understand
146 */
147 vm_object_vndeallocate(object);
148 return error;
149 }
150 }
151 }
152 #endif
153
154 /*
155 * Ok so we couldn't do it all in one vm trick...
156 * so cycle around trying smaller bites..
157 */
158 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
159 if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
160 break;
161 #if 1
162 if ((ioflag & IO_VMIO) == 0 && (vfs_ioopt > 1) && object) {
163 /*
164 * Obviously we didn't finish above, but we
165 * didn't get an error either. Try the same trick again.
166 * but this time we are looping.
167 */
168 int nread, toread;
169 toread = uio->uio_resid;
170 if (toread > bytesinfile)
171 toread = bytesinfile;
172
173 /*
174 * Once again, if there isn't enough for a
175 * whole page, don't try optimising.
176 */
177 if (toread >= PAGE_SIZE) {
178 error = uioread(toread, uio, object, &nread);
179 if ((uio->uio_resid == 0) || (error != 0)) {
180 if ((error == 0 ||
181 uio->uio_resid != orig_resid) &&
182 (vp->v_mount->mnt_flag &
183 MNT_NOATIME) == 0)
184 ip->i_flag |= IN_ACCESS;
185 if (object)
186 vm_object_vndeallocate(object);
187 return error;
188 }
189 /*
190 * To get here we didnt't finish or err.
191 * If we did get some data,
192 * loop to try another bite.
193 */
194 if (nread > 0) {
195 continue;
196 }
197 }
198 }
199 #endif
200
201 lbn = lblkno(fs, uio->uio_offset);
202 nextlbn = lbn + 1;
203 size = BLKSIZE(fs, ip, lbn);
204 blkoffset = blkoff(fs, uio->uio_offset);
205
206 /*
207 * The amount we want to transfer in this iteration is
208 * one FS block less the amount of the data before
209 * our startpoint (duh!)
210 */
211 xfersize = fs->fs_bsize - blkoffset;
212
213 /*
214 * But if we actually want less than the block,
215 * or the file doesn't have a whole block more of data,
216 * then use the lesser number.
217 */
218 if (uio->uio_resid < xfersize)
219 xfersize = uio->uio_resid;
220 if (bytesinfile < xfersize)
221 xfersize = bytesinfile;
222
223 if (lblktosize(fs, nextlbn) >= ip->i_size)
224 /*
225 * Don't do readahead if this is the end of the file.
226 */
227 error = bread(vp, lbn, size, NOCRED, &bp);
228 else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0)
229 /*
230 * Otherwise if we are allowed to cluster,
231 * grab as much as we can.
232 *
233 * XXX This may not be a win if we are not
234 * doing sequential access.
235 */
236 error = cluster_read(vp, ip->i_size, lbn,
237 size, NOCRED, uio->uio_resid, seqcount, &bp);
238 else if (lbn - 1 == vp->v_lastr) {
239 /*
240 * If we are NOT allowed to cluster, then
241 * if we appear to be acting sequentially,
242 * fire off a request for a readahead
243 * as well as a read. Note that the 4th and 5th
244 * arguments point to arrays of the size specified in
245 * the 6th argument.
246 */
247 int nextsize = BLKSIZE(fs, ip, nextlbn);
248 error = breadn(vp, lbn,
249 size, &nextlbn, &nextsize, 1, NOCRED, &bp);
250 } else
251 /*
252 * Failing all of the above, just read what the
253 * user asked for. Interestingly, the same as
254 * the first option above.
255 */
256 error = bread(vp, lbn, size, NOCRED, &bp);
257 if (error) {
258 brelse(bp);
259 bp = NULL;
260 break;
261 }
262
263 /*
264 * Remember where we read so we can see latter if we start
265 * acting sequential.
266 */
267 vp->v_lastr = lbn;
268
269 /*
270 * We should only get non-zero b_resid when an I/O error
271 * has occurred, which should cause us to break above.
272 * However, if the short read did not cause an error,
273 * then we want to ensure that we do not uiomove bad
274 * or uninitialized data.
275 */
276 size -= bp->b_resid;
277 if (size < xfersize) {
278 if (size == 0)
279 break;
280 xfersize = size;
281 }
282
283 if (vfs_ioopt && object &&
284 (bp->b_flags & B_VMIO) &&
285 ((blkoffset & PAGE_MASK) == 0) &&
286 ((xfersize & PAGE_MASK) == 0)) {
287 /*
288 * If VFS IO optimisation is turned on,
289 * and it's an exact page multiple
290 * And a normal VM based op,
291 * then use uiomiveco()
292 */
293 error =
294 uiomoveco((char *)bp->b_data + blkoffset,
295 (int)xfersize, uio, object);
296 } else {
297 /*
298 * otherwise use the general form
299 */
300 error =
301 uiomove((char *)bp->b_data + blkoffset,
302 (int)xfersize, uio);
303 }
304
305 if (error)
306 break;
307
308 if ((ioflag & IO_VMIO) &&
309 (LIST_FIRST(&bp->b_dep) == NULL)) {
310 /*
311 * If there are no dependencies, and
312 * it's VMIO, then we don't need the buf,
313 * mark it available for freeing. The VM has the data.
314 */
315 bp->b_flags |= B_RELBUF;
316 brelse(bp);
317 } else {
318 /*
319 * Otherwise let whoever
320 * made the request take care of
321 * freeing it. We just queue
322 * it onto another list.
323 */
324 bqrelse(bp);
325 }
326 }
327
328 /*
329 * This can only happen in the case of an error
330 * because the loop above resets bp to NULL on each iteration
331 * and on normal completion has not set a new value into it.
332 * so it must have come from a 'break' statement
333 */
334 if (bp != NULL) {
335 if ((ioflag & IO_VMIO) &&
336 (LIST_FIRST(&bp->b_dep) == NULL)) {
337 bp->b_flags |= B_RELBUF;
338 brelse(bp);
339 } else {
340 bqrelse(bp);
341 }
342 }
343
344 if (object)
345 vm_object_vndeallocate(object);
346 if ((error == 0 || uio->uio_resid != orig_resid) &&
347 (vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
348 ip->i_flag |= IN_ACCESS;
349 return (error);
350 }
351
352 /*
353 * Vnode op for writing.
354 */
355 int
356 WRITE(ap)
357 struct vop_write_args /* {
358 struct vnode *a_vp;
359 struct uio *a_uio;
360 int a_ioflag;
361 struct ucred *a_cred;
362 } */ *ap;
363 {
364 register struct vnode *vp;
365 register struct uio *uio;
366 register struct inode *ip;
367 register FS *fs;
368 struct buf *bp;
369 struct proc *p;
370 ufs_daddr_t lbn;
371 off_t osize;
372 int blkoffset, error, extended, flags, ioflag, resid, size, xfersize;
373 vm_object_t object;
374
375 extended = 0;
376 ioflag = ap->a_ioflag;
377 uio = ap->a_uio;
378 vp = ap->a_vp;
379 ip = VTOI(vp);
380
381 object = vp->v_object;
382 if (object)
383 vm_object_reference(object);
384
385 #ifdef DIAGNOSTIC
386 if (uio->uio_rw != UIO_WRITE)
387 panic("%s: mode", WRITE_S);
388 #endif
389
390 switch (vp->v_type) {
391 case VREG:
392 if (ioflag & IO_APPEND)
393 uio->uio_offset = ip->i_size;
394 if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size) {
395 if (object)
396 vm_object_vndeallocate(object);
397 return (EPERM);
398 }
399 /* FALLTHROUGH */
400 case VLNK:
401 break;
402 case VDIR:
403 if ((ioflag & IO_SYNC) == 0)
404 panic("%s: nonsync dir write", WRITE_S);
405 break;
406 default:
407 panic("%s: type", WRITE_S);
408 }
409
410 fs = ip->I_FS;
411 if (uio->uio_offset < 0 ||
412 (u_int64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize) {
413 if (object)
414 vm_object_vndeallocate(object);
415 return (EFBIG);
416 }
417 /*
418 * Maybe this should be above the vnode op call, but so long as
419 * file servers have no limits, I don't think it matters.
420 */
421 p = uio->uio_procp;
422 if (vp->v_type == VREG && p &&
423 uio->uio_offset + uio->uio_resid >
424 p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
425 psignal(p, SIGXFSZ);
426 if (object)
427 vm_object_vndeallocate(object);
428 return (EFBIG);
429 }
430
431 resid = uio->uio_resid;
432 osize = ip->i_size;
433 flags = ioflag & IO_SYNC ? B_SYNC : 0;
434
435 if (object && (object->flags & OBJ_OPT)) {
436 vm_freeze_copyopts(object,
437 OFF_TO_IDX(uio->uio_offset),
438 OFF_TO_IDX(uio->uio_offset + uio->uio_resid + PAGE_MASK));
439 }
440
441 for (error = 0; uio->uio_resid > 0;) {
442 lbn = lblkno(fs, uio->uio_offset);
443 blkoffset = blkoff(fs, uio->uio_offset);
444 xfersize = fs->fs_bsize - blkoffset;
445 if (uio->uio_resid < xfersize)
446 xfersize = uio->uio_resid;
447
448 if (uio->uio_offset + xfersize > ip->i_size)
449 vnode_pager_setsize(vp, uio->uio_offset + xfersize);
450
451 /*
452 * Avoid a data-consistency race between write() and mmap()
453 * by ensuring that newly allocated blocks are zerod. The
454 * race can occur even in the case where the write covers
455 * the entire block.
456 */
457 flags |= B_CLRBUF;
458 #if 0
459 if (fs->fs_bsize > xfersize)
460 flags |= B_CLRBUF;
461 else
462 flags &= ~B_CLRBUF;
463 #endif
464 /* XXX is uio->uio_offset the right thing here? */
465 error = VOP_BALLOC(vp, uio->uio_offset, xfersize,
466 ap->a_cred, flags, &bp);
467 if (error != 0)
468 break;
469
470 if (uio->uio_offset + xfersize > ip->i_size) {
471 ip->i_size = uio->uio_offset + xfersize;
472 extended = 1;
473 }
474
475 size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
476 if (size < xfersize)
477 xfersize = size;
478
479 error =
480 uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
481 if ((ioflag & IO_VMIO) &&
482 (LIST_FIRST(&bp->b_dep) == NULL))
483 bp->b_flags |= B_RELBUF;
484
485 if (ioflag & IO_SYNC) {
486 (void)bwrite(bp);
487 } else if (xfersize + blkoffset == fs->fs_bsize) {
488 if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) {
489 bp->b_flags |= B_CLUSTEROK;
490 cluster_write(bp, ip->i_size);
491 } else {
492 bawrite(bp);
493 }
494 } else {
495 bp->b_flags |= B_CLUSTEROK;
496 bdwrite(bp);
497 }
498 if (error || xfersize == 0)
499 break;
500 ip->i_flag |= IN_CHANGE | IN_UPDATE;
501 }
502 /*
503 * If we successfully wrote any data, and we are not the superuser
504 * we clear the setuid and setgid bits as a precaution against
505 * tampering.
506 */
507 if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
508 ip->i_mode &= ~(ISUID | ISGID);
509 if (error) {
510 if (ioflag & IO_UNIT) {
511 (void)UFS_TRUNCATE(vp, osize,
512 ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
513 uio->uio_offset -= resid - uio->uio_resid;
514 uio->uio_resid = resid;
515 }
516 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
517 error = UFS_UPDATE(vp, 1);
518 if (!error)
519 VN_POLLEVENT(vp, POLLWRITE | (extended ? POLLEXTEND : 0));
520
521 if (object)
522 vm_object_vndeallocate(object);
523
524 return (error);
525 }
526
527
528 /*
529 * get page routine
530 */
531 int
532 ffs_getpages(ap)
533 struct vop_getpages_args *ap;
534 {
535 off_t foff, physoffset;
536 int i, size, bsize;
537 struct vnode *dp, *vp;
538 vm_object_t obj;
539 vm_pindex_t pindex, firstindex;
540 vm_page_t m, mreq;
541 int bbackwards, bforwards;
542 int pbackwards, pforwards;
543 int firstpage;
544 int reqlblkno;
545 daddr_t reqblkno;
546 int poff;
547 int pcount;
548 int rtval;
549 int pagesperblock;
550
551
552 pcount = round_page(ap->a_count) / PAGE_SIZE;
553 mreq = ap->a_m[ap->a_reqpage];
554 firstindex = ap->a_m[0]->pindex;
555
556 /*
557 * if ANY DEV_BSIZE blocks are valid on a large filesystem block
558 * then, the entire page is valid --
559 */
560 if (mreq->valid) {
561 mreq->valid = VM_PAGE_BITS_ALL;
562 for (i = 0; i < pcount; i++) {
563 if (i != ap->a_reqpage) {
564 vm_page_free(ap->a_m[i]);
565 }
566 }
567 return VM_PAGER_OK;
568 }
569
570 vp = ap->a_vp;
571 obj = vp->v_object;
572 bsize = vp->v_mount->mnt_stat.f_iosize;
573 pindex = mreq->pindex;
574 foff = IDX_TO_OFF(pindex) /* + ap->a_offset should be zero */;
575
576 if (bsize < PAGE_SIZE)
577 return vnode_pager_generic_getpages(ap->a_vp, ap->a_m,
578 ap->a_count,
579 ap->a_reqpage);
580
581
582 if (firstindex == 0)
583 vp->v_lastr = 0;
584
585 if ((obj->behavior != OBJ_RANDOM) &&
586 ((firstindex != 0) && (firstindex <= vp->v_lastr) &&
587 ((firstindex + pcount) > vp->v_lastr)) ||
588 (obj->behavior == OBJ_SEQUENTIAL)) {
589 struct uio auio;
590 struct iovec aiov;
591 int error;
592
593 for (i = 0; i < pcount; i++) {
594 m = ap->a_m[i];
595 vm_page_activate(m);
596 vm_page_io_start(m);
597 vm_page_wakeup(m);
598 }
599
600 auio.uio_iov = &aiov;
601 auio.uio_iovcnt = 1;
602 aiov.iov_base = 0;
603 aiov.iov_len = MAXBSIZE;
604 auio.uio_resid = MAXBSIZE;
605 auio.uio_offset = foff;
606 auio.uio_segflg = UIO_NOCOPY;
607 auio.uio_rw = UIO_READ;
608 auio.uio_procp = curproc;
609 error = VOP_READ(vp, &auio,
610 IO_VMIO | ((MAXBSIZE / bsize) << 16), curproc->p_ucred);
611
612 for (i = 0; i < pcount; i++) {
613 m = ap->a_m[i];
614 vm_page_io_finish(m);
615
616 if ((m != mreq) && (m->wire_count == 0) && (m->hold_count == 0) &&
617 (m->valid == 0) && (m->busy == 0) &&
618 (m->flags & PG_BUSY) == 0) {
619 vm_page_busy(m);
620 vm_page_free(m);
621 } else if (m == mreq) {
622 while (m->flags & PG_BUSY) {
623 vm_page_sleep(m, "ffspwt", NULL);
624 }
625 vm_page_busy(m);
626 vp->v_lastr = m->pindex + 1;
627 } else {
628 if (m->wire_count == 0) {
629 if (m->busy || (m->flags & PG_MAPPED) ||
630 (m->flags & (PG_WANTED | PG_BUSY)) == PG_WANTED) {
631 vm_page_activate(m);
632 } else {
633 vm_page_deactivate(m);
634 }
635 }
636 vp->v_lastr = m->pindex + 1;
637 }
638 }
639
640 if (mreq->valid == 0)
641 return VM_PAGER_ERROR;
642
643 mreq->valid = VM_PAGE_BITS_ALL;
644 return VM_PAGER_OK;
645 }
646
647 /*
648 * foff is the file offset of the required page
649 * reqlblkno is the logical block that contains the page
650 * poff is the index of the page into the logical block
651 */
652 reqlblkno = foff / bsize;
653 poff = (foff % bsize) / PAGE_SIZE;
654
655 if ( VOP_BMAP( vp, reqlblkno, &dp, &reqblkno,
656 &bforwards, &bbackwards) || (reqblkno == -1)) {
657 for(i = 0; i < pcount; i++) {
658 if (i != ap->a_reqpage)
659 vm_page_free(ap->a_m[i]);
660 }
661 if (reqblkno == -1) {
662 if ((mreq->flags & PG_ZERO) == 0)
663 vm_page_zero_fill(mreq);
664 mreq->dirty = 0;
665 mreq->valid = VM_PAGE_BITS_ALL;
666 return VM_PAGER_OK;
667 } else {
668 return VM_PAGER_ERROR;
669 }
670 }
671
672 physoffset = (off_t)reqblkno * DEV_BSIZE + poff * PAGE_SIZE;
673 pagesperblock = bsize / PAGE_SIZE;
674 /*
675 * find the first page that is contiguous...
676 * note that pbackwards is the number of pages that are contiguous
677 * backwards.
678 */
679 firstpage = 0;
680 if (ap->a_count) {
681 pbackwards = poff + bbackwards * pagesperblock;
682 if (ap->a_reqpage > pbackwards) {
683 firstpage = ap->a_reqpage - pbackwards;
684 for(i=0;i<firstpage;i++)
685 vm_page_free(ap->a_m[i]);
686 }
687
688 /*
689 * pforwards is the number of pages that are contiguous
690 * after the current page.
691 */
692 pforwards = (pagesperblock - (poff + 1)) +
693 bforwards * pagesperblock;
694 if (pforwards < (pcount - (ap->a_reqpage + 1))) {
695 for( i = ap->a_reqpage + pforwards + 1; i < pcount; i++)
696 vm_page_free(ap->a_m[i]);
697 pcount = ap->a_reqpage + pforwards + 1;
698 }
699
700 /*
701 * number of pages for I/O corrected for the non-contig pages at
702 * the beginning of the array.
703 */
704 pcount -= firstpage;
705 }
706
707 /*
708 * calculate the size of the transfer
709 */
710
711 size = pcount * PAGE_SIZE;
712 vp->v_lastr = mreq->pindex + pcount;
713
714 if ((IDX_TO_OFF(ap->a_m[firstpage]->pindex) + size) >
715 obj->un_pager.vnp.vnp_size)
716 size = obj->un_pager.vnp.vnp_size -
717 IDX_TO_OFF(ap->a_m[firstpage]->pindex);
718
719 physoffset -= foff;
720 rtval = VOP_GETPAGES(dp, &ap->a_m[firstpage], size,
721 (ap->a_reqpage - firstpage), physoffset);
722
723 return (rtval);
724 }
725
726 /*
727 * put page routine
728 *
729 * XXX By default, wimp out... note that a_offset is ignored (and always
730 * XXX has been).
731 */
732 int
733 ffs_putpages(ap)
734 struct vop_putpages_args *ap;
735 {
736 return vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count,
737 ap->a_sync, ap->a_rtvals);
738 }
Cache object: 193849919c04458e4f743d949aed0354
|