FBSD: Lock vm object before vm_page_undirty
[openafs.git] / src / afs / FBSD / osi_vnodeops.c
1 /*
2  * A large chunk of this file appears to be copied directly from
3  * sys/nfsclient/nfs_bio.c, which has the following license:
4  */
5 /*
6  * Copyright (c) 1989, 1993
7  *      The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Rick Macklem at The University of Guelph.
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  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *      @(#)nfs_bio.c   8.9 (Berkeley) 3/30/95
41  */
42 /*
43  * Pursuant to a statement of U.C. Berkeley dated 1999-07-22, this license
44  * is amended to drop clause (3) above.
45  */
46
47 #include <afsconfig.h>
48 #include <afs/param.h>
49
50
51 #include <afs/sysincludes.h>    /* Standard vendor system headers */
52 #include <afsincludes.h>        /* Afs-based standard headers */
53 #include <afs/afs_stats.h>      /* statistics */
54 #include <sys/malloc.h>
55 #include <sys/namei.h>
56 #include <sys/unistd.h>
57 #if __FreeBSD_version >= 1000030
58 #include <sys/rwlock.h>
59 #endif
60 #include <vm/vm_page.h>
61 #include <vm/vm_object.h>
62 #include <vm/vm_pager.h>
63 #include <vm/vnode_pager.h>
64 #include <sys/vmmeter.h>
65 extern int afs_pbuf_freecnt;
66
67 #define GETNAME()       \
68     struct componentname *cnp = ap->a_cnp; \
69     char *name; \
70     name = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK); \
71     memcpy(name, cnp->cn_nameptr, cnp->cn_namelen); \
72     name[cnp->cn_namelen] = '\0'
73
74 #define DROPNAME() free(name, M_TEMP)
75
76 #ifdef LINK_MAX
77 # define AFS_LINK_MAX LINK_MAX
78 #else
79 # define AFS_LINK_MAX (32767)
80 #endif
81
82 /*
83  * Here we define compatibility functions/macros for interfaces that
84  * have changed between different FreeBSD versions.
85  */
86 static __inline void ma_vm_page_lock_queues(void) {};
87 static __inline void ma_vm_page_unlock_queues(void) {};
88 static __inline void ma_vm_page_lock(vm_page_t m) { vm_page_lock(m); };
89 static __inline void ma_vm_page_unlock(vm_page_t m) { vm_page_unlock(m); };
90
91 #if __FreeBSD_version >= 1000030
92 #define AFS_VM_OBJECT_WLOCK(o)  VM_OBJECT_WLOCK(o)
93 #define AFS_VM_OBJECT_WUNLOCK(o)        VM_OBJECT_WUNLOCK(o)
94 #else
95 #define AFS_VM_OBJECT_WLOCK(o)  VM_OBJECT_LOCK(o)
96 #define AFS_VM_OBJECT_WUNLOCK(o)        VM_OBJECT_UNLOCK(o)
97 #endif
98
99 #ifdef VM_CNT_ADD
100 # define AFS_VM_CNT_ADD(var, x) VM_CNT_ADD(var, x)
101 # define AFS_VM_CNT_INC(var)    VM_CNT_INC(var)
102 #else
103 # define AFS_VM_CNT_ADD(var, x) PCPU_ADD(cnt.var, x)
104 # define AFS_VM_CNT_INC(var)    PCPU_INC(cnt.var)
105 #endif
106
107 /*
108  * Mosty copied from sys/ufs/ufs/ufs_vnops.c:ufs_pathconf().
109  * We should know the correct answers to these questions with
110  * respect to the AFS protocol (which may differ from the UFS
111  * values) but for the moment this will do.
112  */
113 static int
114 afs_vop_pathconf(struct vop_pathconf_args *ap)
115 {
116         int error;
117
118         error = 0;
119         switch (ap->a_name) {
120         case _PC_LINK_MAX:
121                 *ap->a_retval = AFS_LINK_MAX;
122                 break;
123         case _PC_NAME_MAX:
124                 *ap->a_retval = NAME_MAX;
125                 break;
126         case _PC_PATH_MAX:
127                 *ap->a_retval = PATH_MAX;
128                 break;
129         case _PC_PIPE_BUF:
130                 *ap->a_retval = PIPE_BUF;
131                 break;
132         case _PC_CHOWN_RESTRICTED:
133                 *ap->a_retval = 1;
134                 break;
135         case _PC_NO_TRUNC:
136                 *ap->a_retval = 1;
137                 break;
138 #ifdef _PC_ACL_EXTENDED
139         case _PC_ACL_EXTENDED:
140                 *ap->a_retval = 0;
141                 break;
142         case _PC_ACL_PATH_MAX:
143                 *ap->a_retval = 3;
144                 break;
145 #endif
146 #ifdef _PC_MAC_PRESENT
147         case _PC_MAC_PRESENT:
148                 *ap->a_retval = 0;
149                 break;
150 #endif
151 #ifdef _PC_ASYNC_IO
152         case _PC_ASYNC_IO:
153                 /* _PC_ASYNC_IO should have been handled by upper layers. */
154                 KASSERT(0, ("_PC_ASYNC_IO should not get here"));
155                 error = EINVAL;
156                 break;
157         case _PC_PRIO_IO:
158                 *ap->a_retval = 0;
159                 break;
160         case _PC_SYNC_IO:
161                 *ap->a_retval = 0;
162                 break;
163 #endif
164 #ifdef _PC_ALLOC_SIZE_MIN
165         case _PC_ALLOC_SIZE_MIN:
166                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize;
167                 break;
168 #endif
169 #ifdef _PC_FILESIZEBITS
170         case _PC_FILESIZEBITS:
171                 *ap->a_retval = 32; /* XXX */
172                 break;
173 #endif
174 #ifdef _PC_REC_INCR_XFER_SIZE
175         case _PC_REC_INCR_XFER_SIZE:
176                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
177                 break;
178         case _PC_REC_MAX_XFER_SIZE:
179                 *ap->a_retval = -1; /* means ``unlimited'' */
180                 break;
181         case _PC_REC_MIN_XFER_SIZE:
182                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
183                 break;
184         case _PC_REC_XFER_ALIGN:
185                 *ap->a_retval = PAGE_SIZE;
186                 break;
187 #endif
188 #ifdef _PC_SYMLINK_MAX
189         case _PC_SYMLINK_MAX:
190                 *ap->a_retval = MAXPATHLEN;
191                 break;
192 #endif
193         default:
194                 error = EINVAL;
195                 break;
196         }
197         return (error);
198 }
199
200 static int
201 afs_vop_lookup(ap)
202      struct vop_lookup_args     /* {
203                                  * struct vnodeop_desc * a_desc;
204                                  * struct vnode *a_dvp;
205                                  * struct vnode **a_vpp;
206                                  * struct componentname *a_cnp;
207                                  * } */ *ap;
208 {
209     int error;
210     struct vcache *vcp;
211     struct vnode *vp, *dvp;
212     int flags = ap->a_cnp->cn_flags;
213
214     dvp = ap->a_dvp;
215     if (dvp->v_type != VDIR) {
216         return ENOTDIR;
217     }
218
219     if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT))
220         return EIO;
221
222     GETNAME();
223
224 #if __FreeBSD_version < 1000021
225     cnp->cn_flags |= MPSAFE; /* steel */
226 #endif
227
228     /*
229      * Locking rules:
230      *
231      * - 'dvp' is locked by our caller. We must return it locked, whether we
232      * return success or error.
233      *
234      * - If the lookup succeeds, 'vp' must be locked before we return.
235      *
236      * - If we lock multiple vnodes, parent vnodes must be locked before
237      * children vnodes.
238      *
239      * As a result, looking up the parent directory (if 'flags' has ISDOTDOT
240      * set) is a bit of a special case. In that case, we must unlock 'dvp'
241      * before performing the lookup, since the lookup operation may lock the
242      * target vnode, and the target vnode is the parent of 'dvp' (so we must
243      * lock 'dvp' after locking the target vnode).
244      */
245
246     if (flags & ISDOTDOT)
247         VOP_UNLOCK(dvp, 0);
248
249     AFS_GLOCK();
250     error = afs_lookup(VTOAFS(dvp), name, &vcp, cnp->cn_cred);
251     AFS_GUNLOCK();
252
253     if (error) {
254         if (flags & ISDOTDOT)
255             vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
256         if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)
257             && (flags & ISLASTCN) && error == ENOENT)
258             error = EJUSTRETURN;
259         if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
260             cnp->cn_flags |= SAVENAME;
261         DROPNAME();
262         *ap->a_vpp = 0;
263         return (error);
264     }
265     vp = AFSTOV(vcp);           /* always get a node if no error */
266
267     if (flags & ISDOTDOT) {
268         /* Must lock 'vp' before 'dvp', since 'vp' is the parent vnode. */
269         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
270         vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
271     } else if (vp == dvp) {
272         /* they're the same; afs_lookup() already ref'ed the leaf.
273          * It came in locked, so we don't need to ref OR lock it */
274     } else {
275         vn_lock(vp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
276     }
277     *ap->a_vpp = vp;
278
279     if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
280         cnp->cn_flags |= SAVENAME;
281
282     DROPNAME();
283     return error;
284 }
285
286 static int
287 afs_vop_create(ap)
288      struct vop_create_args     /* {
289                                  * struct vnode *a_dvp;
290                                  * struct vnode **a_vpp;
291                                  * struct componentname *a_cnp;
292                                  * struct vattr *a_vap;
293                                  * } */ *ap;
294 {
295     int error = 0;
296     struct vcache *vcp;
297     struct vnode *dvp = ap->a_dvp;
298     GETNAME();
299
300     AFS_GLOCK();
301     error =
302         afs_create(VTOAFS(dvp), name, ap->a_vap,
303                    ap->a_vap->va_vaflags & VA_EXCLUSIVE ? EXCL : NONEXCL,
304                    ap->a_vap->va_mode, &vcp, cnp->cn_cred);
305     AFS_GUNLOCK();
306     if (error) {
307         DROPNAME();
308         return (error);
309     }
310
311     if (vcp) {
312         *ap->a_vpp = AFSTOV(vcp);
313         vn_lock(AFSTOV(vcp), LK_EXCLUSIVE | LK_RETRY);
314     } else
315         *ap->a_vpp = 0;
316
317     DROPNAME();
318     return error;
319 }
320
321 static int
322 afs_vop_mknod(ap)
323      struct vop_mknod_args      /* {
324                                  * struct vnode *a_dvp;
325                                  * struct vnode **a_vpp;
326                                  * struct componentname *a_cnp;
327                                  * struct vattr *a_vap;
328                                  * } */ *ap;
329 {
330     return (ENODEV);
331 }
332
333 static int
334 afs_vop_open(ap)
335      struct vop_open_args       /* {
336                                  * struct vnode *a_vp;
337                                  * int  a_mode;
338                                  * struct ucred *a_cred;
339                                  * struct thread *a_td;
340                                  * struct file *a_fp;
341                                  * } */ *ap;
342 {
343     int error;
344     struct vcache *vc = VTOAFS(ap->a_vp);
345
346     AFS_GLOCK();
347     error = afs_open(&vc, ap->a_mode, ap->a_cred);
348 #ifdef DIAGNOSTIC
349     if (AFSTOV(vc) != ap->a_vp)
350         panic("AFS open changed vnode!");
351 #endif
352     AFS_GUNLOCK();
353     vnode_create_vobject(ap->a_vp, vc->f.m.Length, ap->a_td);
354     osi_FlushPages(vc, ap->a_cred);
355     return error;
356 }
357
358 static int
359 afs_vop_close(ap)
360      struct vop_close_args      /* {
361                                  * struct vnode *a_vp;
362                                  * int  a_fflag;
363                                  * struct ucred *a_cred;
364                                  * struct thread *a_td;
365                                  * } */ *ap;
366 {
367     int code, iflag;
368     struct vnode *vp = ap->a_vp;
369     struct vcache *avc = VTOAFS(vp);
370
371     VI_LOCK(vp);
372     iflag = vp->v_iflag & VI_DOOMED;
373     VI_UNLOCK(vp);
374     if (iflag & VI_DOOMED) {
375         /* osi_FlushVCache (correctly) calls vgone() on recycled vnodes, we don't
376          * have an afs_close to process, in that case */
377         if (avc->opens != 0)
378             panic("afs_vop_close: doomed vnode %p has vcache %p with non-zero opens %d\n",
379                   vp, avc, avc->opens);
380         return 0;
381     }
382
383     AFS_GLOCK();
384     if (ap->a_cred)
385         code = afs_close(avc, ap->a_fflag, ap->a_cred);
386     else
387         code = afs_close(avc, ap->a_fflag, afs_osi_credp);
388     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
389     AFS_GUNLOCK();
390     return code;
391 }
392
393 static int
394 afs_vop_access(ap)
395      struct vop_access_args     /* {
396                                  * struct vnode *a_vp;
397                                  * accmode_t a_accmode;
398                                  * struct ucred *a_cred;
399                                  * struct thread *a_td;
400                                  * } */ *ap;
401 {
402     int code;
403     AFS_GLOCK();
404     code = afs_access(VTOAFS(ap->a_vp), ap->a_accmode, ap->a_cred);
405     AFS_GUNLOCK();
406     return code;
407 }
408
409 static int
410 afs_vop_getattr(ap)
411      struct vop_getattr_args    /* {
412                                  * struct vnode *a_vp;
413                                  * struct vattr *a_vap;
414                                  * struct ucred *a_cred;
415                                  * } */ *ap;
416 {
417     int code;
418
419     AFS_GLOCK();
420     code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, ap->a_cred);
421     AFS_GUNLOCK();
422
423     return code;
424 }
425
426 static int
427 afs_vop_setattr(ap)
428      struct vop_setattr_args    /* {
429                                  * struct vnode *a_vp;
430                                  * struct vattr *a_vap;
431                                  * struct ucred *a_cred;
432                                  * } */ *ap;
433 {
434     int code;
435     AFS_GLOCK();
436     code = afs_setattr(VTOAFS(ap->a_vp), ap->a_vap, ap->a_cred);
437     AFS_GUNLOCK();
438     return code;
439 }
440
441 static int
442 afs_vop_read(ap)
443      struct vop_read_args       /* {
444                                  * struct vnode *a_vp;
445                                  * struct uio *a_uio;
446                                  * int a_ioflag;
447                                  * struct ucred *a_cred;
448                                  * 
449                                  * } */ *ap;
450 {
451     int code;
452     struct vcache *avc = VTOAFS(ap->a_vp);
453     AFS_GLOCK();
454     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
455     code = afs_read(avc, ap->a_uio, ap->a_cred, 0);
456     AFS_GUNLOCK();
457     return code;
458 }
459
460 /* struct vop_getpages_args {
461  *      struct vnode *a_vp;
462  *      vm_page_t *a_m;
463  *      int a_count;
464  *      int *a_rbehind;
465  *      int *a_rahead;
466  * };
467  */
468 static int
469 afs_vop_getpages(struct vop_getpages_args *ap)
470 {
471     int code;
472     int i, nextoff, size, toff, npages, count;
473     struct uio uio;
474     struct iovec iov;
475     struct buf *bp;
476     vm_offset_t kva;
477     vm_object_t object;
478     vm_page_t *pages;
479     struct vnode *vp;
480     struct vcache *avc;
481
482     memset(&uio, 0, sizeof(uio));
483     memset(&iov, 0, sizeof(iov));
484
485     vp = ap->a_vp;
486     avc = VTOAFS(vp);
487     pages = ap->a_m;
488 #ifdef FBSD_VOP_GETPAGES_BUSIED
489     npages = ap->a_count;
490     if (ap->a_rbehind)
491         *ap->a_rbehind = 0;
492     if (ap->a_rahead)
493         *ap->a_rahead = 0;
494 #else
495     npages = btoc(ap->a_count);
496 #endif
497
498     if ((object = vp->v_object) == NULL) {
499         printf("afs_getpages: called with non-merged cache vnode??\n");
500         return VM_PAGER_ERROR;
501     }
502
503     /*
504      * If the requested page is partially valid, just return it and
505      * allow the pager to zero-out the blanks.  Partially valid pages
506      * can only occur at the file EOF.
507      */
508     {
509 #ifdef FBSD_VOP_GETPAGES_BUSIED
510         AFS_VM_OBJECT_WLOCK(object);
511         ma_vm_page_lock_queues();
512         if(pages[npages - 1]->valid != 0) {
513             if (--npages == 0) {
514                 ma_vm_page_unlock_queues();
515                 AFS_VM_OBJECT_WUNLOCK(object);
516                 return (VM_PAGER_OK);
517             }
518         }
519 #else
520         vm_page_t m = pages[ap->a_reqpage];
521         AFS_VM_OBJECT_WLOCK(object);
522         ma_vm_page_lock_queues();
523         if (m->valid != 0) {
524             /* handled by vm_fault now        */
525             /* vm_page_zero_invalid(m, TRUE); */
526             for (i = 0; i < npages; ++i) {
527                 if (i != ap->a_reqpage) {
528                     ma_vm_page_lock(pages[i]);
529                     vm_page_free(pages[i]);
530                     ma_vm_page_unlock(pages[i]);
531                 }
532             }
533             ma_vm_page_unlock_queues();
534             AFS_VM_OBJECT_WUNLOCK(object);
535             return (0);
536         }
537 #endif
538         ma_vm_page_unlock_queues();
539         AFS_VM_OBJECT_WUNLOCK(object);
540     }
541     bp = getpbuf(&afs_pbuf_freecnt);
542
543     kva = (vm_offset_t) bp->b_data;
544     pmap_qenter(kva, pages, npages);
545     AFS_VM_CNT_INC(v_vnodein);
546     AFS_VM_CNT_ADD(v_vnodepgsin, npages);
547
548 #ifdef FBSD_VOP_GETPAGES_BUSIED
549     count = ctob(npages);
550 #else
551     count = ap->a_count;
552 #endif
553     iov.iov_base = (caddr_t) kva;
554     iov.iov_len = count;
555     uio.uio_iov = &iov;
556     uio.uio_iovcnt = 1;
557     uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
558     uio.uio_resid = count;
559     uio.uio_segflg = UIO_SYSSPACE;
560     uio.uio_rw = UIO_READ;
561     uio.uio_td = curthread;
562
563     AFS_GLOCK();
564     osi_FlushPages(avc, osi_curcred()); /* hold GLOCK, but not basic vnode lock */
565     code = afs_read(avc, &uio, osi_curcred(), 0);
566     AFS_GUNLOCK();
567     pmap_qremove(kva, npages);
568
569     relpbuf(bp, &afs_pbuf_freecnt);
570
571     if (code && (uio.uio_resid == count)) {
572 #ifndef FBSD_VOP_GETPAGES_BUSIED
573         AFS_VM_OBJECT_WLOCK(object);
574         ma_vm_page_lock_queues();
575         for (i = 0; i < npages; ++i) {
576             if (i != ap->a_reqpage)
577                 vm_page_free(pages[i]);
578         }
579         ma_vm_page_unlock_queues();
580         AFS_VM_OBJECT_WUNLOCK(object);
581 #endif
582         return VM_PAGER_ERROR;
583     }
584
585     size = count - uio.uio_resid;
586     AFS_VM_OBJECT_WLOCK(object);
587     ma_vm_page_lock_queues();
588     for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
589         vm_page_t m;
590         nextoff = toff + PAGE_SIZE;
591         m = pages[i];
592
593         /* XXX not in nfsclient? */
594         m->flags &= ~PG_ZERO;
595
596         if (nextoff <= size) {
597             /*
598              * Read operation filled an entire page
599              */
600             m->valid = VM_PAGE_BITS_ALL;
601             KASSERT(m->dirty == 0, ("afs_getpages: page %p is dirty", m));
602         } else if (size > toff) {
603             /*
604              * Read operation filled a partial page.
605              */
606             m->valid = 0;
607             vm_page_set_validclean(m, 0, size - toff);
608             KASSERT(m->dirty == 0, ("afs_getpages: page %p is dirty", m));
609         }
610
611 #ifndef FBSD_VOP_GETPAGES_BUSIED
612         if (i != ap->a_reqpage) {
613 #if __FreeBSD_version >= 1000042
614             vm_page_readahead_finish(m);
615 #else
616             /*
617              * Whether or not to leave the page activated is up in
618              * the air, but we should put the page on a page queue
619              * somewhere (it already is in the object).  Result:
620              * It appears that emperical results show that
621              * deactivating pages is best.
622              */
623
624             /*
625              * Just in case someone was asking for this page we
626              * now tell them that it is ok to use.
627              */
628             if (!code) {
629                 if (m->oflags & VPO_WANTED) {
630                     ma_vm_page_lock(m);
631                     vm_page_activate(m);
632                     ma_vm_page_unlock(m);
633                 }
634                 else {
635                     ma_vm_page_lock(m);
636                     vm_page_deactivate(m);
637                     ma_vm_page_unlock(m);
638                 }
639                 vm_page_wakeup(m);
640             } else {
641                 ma_vm_page_lock(m);
642                 vm_page_free(m);
643                 ma_vm_page_unlock(m);
644             }
645 #endif  /* __FreeBSD_version 1000042 */
646         }
647 #endif   /* ndef FBSD_VOP_GETPAGES_BUSIED */
648     }
649     ma_vm_page_unlock_queues();
650     AFS_VM_OBJECT_WUNLOCK(object);
651     return VM_PAGER_OK;
652 }
653
654 static int
655 afs_vop_write(ap)
656      struct vop_write_args      /* {
657                                  * struct vnode *a_vp;
658                                  * struct uio *a_uio;
659                                  * int a_ioflag;
660                                  * struct ucred *a_cred;
661                                  * } */ *ap;
662 {
663     int code;
664     struct vcache *avc = VTOAFS(ap->a_vp);
665     AFS_GLOCK();
666     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
667     code =
668         afs_write(VTOAFS(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_cred, 0);
669     AFS_GUNLOCK();
670     return code;
671 }
672
673 /*-
674  * struct vop_putpages_args {
675  *      struct vnode *a_vp;
676  *      vm_page_t *a_m;
677  *      int a_count;
678  *      int a_sync;
679  *      int *a_rtvals;
680  *      vm_oofset_t a_offset;
681  * };
682  */
683 /*
684  * All of the pages passed to us in ap->a_m[] are already marked as busy,
685  * so there is no additional locking required to set their flags.  -GAW
686  */
687 static int
688 afs_vop_putpages(struct vop_putpages_args *ap)
689 {
690     int code;
691     int i, size, npages, sync;
692     struct uio uio;
693     struct iovec iov;
694     struct buf *bp;
695     vm_offset_t kva;
696     struct vnode *vp;
697     struct vcache *avc;
698
699     memset(&uio, 0, sizeof(uio));
700     memset(&iov, 0, sizeof(iov));
701
702     vp = ap->a_vp;
703     avc = VTOAFS(vp);
704     /* Perhaps these two checks should just be KASSERTs instead... */
705     if (vp->v_object == NULL) {
706         printf("afs_putpages: called with non-merged cache vnode??\n");
707         return VM_PAGER_ERROR;  /* XXX I think this is insufficient */
708     }
709     if (vType(avc) != VREG) {
710         printf("afs_putpages: not VREG");
711         return VM_PAGER_ERROR;  /* XXX I think this is insufficient */
712     }
713     npages = btoc(ap->a_count);
714     for (i = 0; i < npages; i++)
715         ap->a_rtvals[i] = VM_PAGER_AGAIN;
716     bp = getpbuf(&afs_pbuf_freecnt);
717
718     kva = (vm_offset_t) bp->b_data;
719     pmap_qenter(kva, ap->a_m, npages);
720     AFS_VM_CNT_INC(v_vnodeout);
721     AFS_VM_CNT_ADD(v_vnodepgsout, ap->a_count);
722
723     iov.iov_base = (caddr_t) kva;
724     iov.iov_len = ap->a_count;
725     uio.uio_iov = &iov;
726     uio.uio_iovcnt = 1;
727     uio.uio_offset = IDX_TO_OFF(ap->a_m[0]->pindex);
728     uio.uio_resid = ap->a_count;
729     uio.uio_segflg = UIO_SYSSPACE;
730     uio.uio_rw = UIO_WRITE;
731     uio.uio_td = curthread;
732     sync = IO_VMIO;
733     if (ap->a_sync & VM_PAGER_PUT_SYNC)
734         sync |= IO_SYNC;
735     /*if (ap->a_sync & VM_PAGER_PUT_INVAL)
736      * sync |= IO_INVAL; */
737
738     AFS_GLOCK();
739     code = afs_write(avc, &uio, sync, osi_curcred(), 0);
740     AFS_GUNLOCK();
741
742     pmap_qremove(kva, npages);
743     relpbuf(bp, &afs_pbuf_freecnt);
744
745     if (!code) {
746         AFS_VM_OBJECT_WLOCK(vp->v_object);
747         size = ap->a_count - uio.uio_resid;
748         for (i = 0; i < round_page(size) / PAGE_SIZE; i++) {
749             ap->a_rtvals[i] = VM_PAGER_OK;
750             vm_page_undirty(ap->a_m[i]);
751         }
752         AFS_VM_OBJECT_WUNLOCK(vp->v_object);
753     }
754     return ap->a_rtvals[0];
755 }
756
757 static int
758 afs_vop_ioctl(ap)
759      struct vop_ioctl_args      /* {
760                                  * struct vnode *a_vp;
761                                  * u_long a_command;
762                                  * void *a_data;
763                                  * int  a_fflag;
764                                  * struct ucred *a_cred;
765                                  * struct thread *a_td;
766                                  * } */ *ap;
767 {
768     struct vcache *tvc = VTOAFS(ap->a_vp);
769     int error = 0;
770
771     /* in case we ever get in here... */
772
773     AFS_STATCNT(afs_ioctl);
774     if (((ap->a_command >> 8) & 0xff) == 'V') {
775         /* This is a VICEIOCTL call */
776         AFS_GLOCK();
777         error = HandleIoctl(tvc, ap->a_command, ap->a_data);
778         AFS_GUNLOCK();
779         return (error);
780     } else {
781         /* No-op call; just return. */
782         return (ENOTTY);
783     }
784 }
785
786 static int
787 afs_vop_fsync(ap)
788      struct vop_fsync_args      /* {
789                                  * struct vnode *a_vp;
790                                  * int a_waitfor;
791                                  * struct thread *td;
792                                  * } */ *ap;
793 {
794     int error;
795     struct vnode *vp = ap->a_vp;
796
797     AFS_GLOCK();
798     /*vflushbuf(vp, wait); */
799     error = afs_fsync(VTOAFS(vp), ap->a_td->td_ucred);
800     AFS_GUNLOCK();
801     return error;
802 }
803
804 static int
805 afs_vop_remove(ap)
806      struct vop_remove_args     /* {
807                                  * struct vnode *a_dvp;
808                                  * struct vnode *a_vp;
809                                  * struct componentname *a_cnp;
810                                  * } */ *ap;
811 {
812     int error = 0;
813     struct vnode *vp = ap->a_vp;
814     struct vnode *dvp = ap->a_dvp;
815
816     GETNAME();
817     AFS_GLOCK();
818     error = afs_remove(VTOAFS(dvp), name, cnp->cn_cred);
819     AFS_GUNLOCK();
820     cache_purge(vp);
821     DROPNAME();
822     return error;
823 }
824
825 static int
826 afs_vop_link(ap)
827      struct vop_link_args       /* {
828                                  * struct vnode *a_vp;
829                                  * struct vnode *a_tdvp;
830                                  * struct componentname *a_cnp;
831                                  * } */ *ap;
832 {
833     int error = 0;
834     struct vnode *dvp = ap->a_tdvp;
835     struct vnode *vp = ap->a_vp;
836
837     GETNAME();
838     if (dvp->v_mount != vp->v_mount) {
839         error = EXDEV;
840         goto out;
841     }
842     if (vp->v_type == VDIR) {
843         error = EISDIR;
844         goto out;
845     }
846     if ((error = vn_lock(vp, LK_CANRECURSE | LK_EXCLUSIVE)) != 0) {
847         goto out;
848     }
849     AFS_GLOCK();
850     error = afs_link(VTOAFS(vp), VTOAFS(dvp), name, cnp->cn_cred);
851     AFS_GUNLOCK();
852     if (dvp != vp)
853         VOP_UNLOCK(vp, 0);
854   out:
855     DROPNAME();
856     return error;
857 }
858
859 static int
860 afs_vop_rename(ap)
861      struct vop_rename_args     /* {
862                                  * struct vnode *a_fdvp;
863                                  * struct vnode *a_fvp;
864                                  * struct componentname *a_fcnp;
865                                  * struct vnode *a_tdvp;
866                                  * struct vnode *a_tvp;
867                                  * struct componentname *a_tcnp;
868                                  * } */ *ap;
869 {
870     int error = 0;
871     struct componentname *fcnp = ap->a_fcnp;
872     char *fname;
873     struct componentname *tcnp = ap->a_tcnp;
874     char *tname;
875     struct vnode *tvp = ap->a_tvp;
876     struct vnode *tdvp = ap->a_tdvp;
877     struct vnode *fvp = ap->a_fvp;
878     struct vnode *fdvp = ap->a_fdvp;
879
880     /*
881      * Check for cross-device rename.
882      */
883     if ((fvp->v_mount != tdvp->v_mount)
884         || (tvp && (fvp->v_mount != tvp->v_mount))) {
885         error = EXDEV;
886       abortit:
887         if (tdvp == tvp)
888             vrele(tdvp);
889         else
890             vput(tdvp);
891         if (tvp)
892             vput(tvp);
893         vrele(fdvp);
894         vrele(fvp);
895         return (error);
896     }
897     /*
898      * if fvp == tvp, we're just removing one name of a pair of
899      * directory entries for the same element.  convert call into rename.
900      ( (pinched from FreeBSD 4.4's ufs_rename())
901      
902      */
903     if (fvp == tvp) {
904         if (fvp->v_type == VDIR) {
905             error = EINVAL;
906             goto abortit;
907         }
908
909         /* Release destination completely. */
910         vput(tdvp);
911         vput(tvp);
912
913         /* Delete source. */
914         vrele(fdvp);
915         vrele(fvp);
916         fcnp->cn_flags &= ~MODMASK;
917         fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
918         if ((fcnp->cn_flags & SAVESTART) == 0)
919             panic("afs_rename: lost from startdir");
920         fcnp->cn_nameiop = DELETE;
921         VREF(fdvp);
922         error = relookup(fdvp, &fvp, fcnp);
923         if (error == 0)
924             vrele(fdvp);
925         if (fvp == NULL) {
926             return (ENOENT);
927         }
928
929         error = VOP_REMOVE(fdvp, fvp, fcnp);
930         if (fdvp == fvp)
931             vrele(fdvp);
932         else
933             vput(fdvp);
934         vput(fvp);
935         return (error);
936     }
937     if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
938         goto abortit;
939
940     fname = malloc(fcnp->cn_namelen + 1, M_TEMP, M_WAITOK);
941     memcpy(fname, fcnp->cn_nameptr, fcnp->cn_namelen);
942     fname[fcnp->cn_namelen] = '\0';
943     tname = malloc(tcnp->cn_namelen + 1, M_TEMP, M_WAITOK);
944     memcpy(tname, tcnp->cn_nameptr, tcnp->cn_namelen);
945     tname[tcnp->cn_namelen] = '\0';
946
947
948     AFS_GLOCK();
949     /* XXX use "from" or "to" creds? NFS uses "to" creds */
950     error =
951         afs_rename(VTOAFS(fdvp), fname, VTOAFS(tdvp), tname, tcnp->cn_cred);
952     AFS_GUNLOCK();
953
954     free(fname, M_TEMP);
955     free(tname, M_TEMP);
956     if (tdvp == tvp)
957         vrele(tdvp);
958     else
959         vput(tdvp);
960     if (tvp)
961         vput(tvp);
962     vrele(fdvp);
963     vput(fvp);
964     return error;
965 }
966
967 static int
968 afs_vop_mkdir(ap)
969      struct vop_mkdir_args      /* {
970                                  * struct vnode *a_dvp;
971                                  * struct vnode **a_vpp;
972                                  * struct componentname *a_cnp;
973                                  * struct vattr *a_vap;
974                                  * } */ *ap;
975 {
976     struct vnode *dvp = ap->a_dvp;
977     struct vattr *vap = ap->a_vap;
978     int error = 0;
979     struct vcache *vcp;
980
981     GETNAME();
982 #ifdef DIAGNOSTIC
983     if ((cnp->cn_flags & HASBUF) == 0)
984         panic("afs_vop_mkdir: no name");
985 #endif
986     AFS_GLOCK();
987     error = afs_mkdir(VTOAFS(dvp), name, vap, &vcp, cnp->cn_cred);
988     AFS_GUNLOCK();
989     if (error) {
990         DROPNAME();
991         return (error);
992     }
993     if (vcp) {
994         *ap->a_vpp = AFSTOV(vcp);
995         vn_lock(AFSTOV(vcp), LK_EXCLUSIVE | LK_RETRY);
996     } else
997         *ap->a_vpp = 0;
998     DROPNAME();
999     return error;
1000 }
1001
1002 static int
1003 afs_vop_rmdir(ap)
1004      struct vop_rmdir_args      /* {
1005                                  * struct vnode *a_dvp;
1006                                  * struct vnode *a_vp;
1007                                  * struct componentname *a_cnp;
1008                                  * } */ *ap;
1009 {
1010     int error = 0;
1011     struct vnode *dvp = ap->a_dvp;
1012
1013     GETNAME();
1014     AFS_GLOCK();
1015     error = afs_rmdir(VTOAFS(dvp), name, cnp->cn_cred);
1016     AFS_GUNLOCK();
1017     DROPNAME();
1018     return error;
1019 }
1020
1021 /* struct vop_symlink_args {
1022  *      struct vnode *a_dvp;
1023  *      struct vnode **a_vpp;
1024  *      struct componentname *a_cnp;
1025  *      struct vattr *a_vap;
1026  *      char *a_target;
1027  * };
1028  */
1029 static int
1030 afs_vop_symlink(struct vop_symlink_args *ap)
1031 {
1032     struct vnode *dvp;
1033     struct vnode *newvp;
1034     struct vcache *vcp;
1035     int error;
1036
1037     GETNAME();
1038     AFS_GLOCK();
1039
1040     dvp = ap->a_dvp;
1041     newvp = NULL;
1042
1043     error =
1044         afs_symlink(VTOAFS(dvp), name, ap->a_vap, ap->a_target, NULL,
1045                     cnp->cn_cred);
1046     if (error == 0) {
1047         error = afs_lookup(VTOAFS(dvp), name, &vcp, cnp->cn_cred);
1048         if (error == 0) {
1049             newvp = AFSTOV(vcp);
1050             vn_lock(newvp, LK_EXCLUSIVE | LK_RETRY);
1051         }
1052     }
1053     AFS_GUNLOCK();
1054     DROPNAME();
1055     *(ap->a_vpp) = newvp;
1056     return error;
1057 }
1058
1059 static int
1060 afs_vop_readdir(ap)
1061      struct vop_readdir_args    /* {
1062                                  * struct vnode *a_vp;
1063                                  * struct uio *a_uio;
1064                                  * struct ucred *a_cred;
1065                                  * int *a_eofflag;
1066                                  * u_long *a_cookies;
1067                                  * int ncookies;
1068                                  * } */ *ap;
1069 {
1070     int error;
1071     off_t off;
1072 /*    printf("readdir %x cookies %x ncookies %d\n", ap->a_vp, ap->a_cookies,
1073            ap->a_ncookies); */
1074     off = ap->a_uio->uio_offset;
1075     AFS_GLOCK();
1076     error =
1077         afs_readdir(VTOAFS(ap->a_vp), ap->a_uio, ap->a_cred, ap->a_eofflag);
1078     AFS_GUNLOCK();
1079     if (!error && ap->a_ncookies != NULL) {
1080         struct uio *uio = ap->a_uio;
1081         const struct dirent *dp, *dp_start, *dp_end;
1082         int ncookies;
1083         u_long *cookies, *cookiep;
1084
1085         if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
1086             panic("afs_readdir: burned cookies");
1087         dp = (const struct dirent *)
1088             ((const char *)uio->uio_iov->iov_base - (uio->uio_offset - off));
1089
1090         dp_end = (const struct dirent *)uio->uio_iov->iov_base;
1091         for (dp_start = dp, ncookies = 0; dp < dp_end;
1092              dp = (const struct dirent *)((const char *)dp + dp->d_reclen))
1093             ncookies++;
1094
1095         cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
1096                M_WAITOK);
1097         for (dp = dp_start, cookiep = cookies; dp < dp_end;
1098              dp = (const struct dirent *)((const char *)dp + dp->d_reclen)) {
1099             off += dp->d_reclen;
1100             *cookiep++ = off;
1101         }
1102         *ap->a_cookies = cookies;
1103         *ap->a_ncookies = ncookies;
1104     }
1105
1106     return error;
1107 }
1108
1109 static int
1110 afs_vop_readlink(ap)
1111      struct vop_readlink_args   /* {
1112                                  * struct vnode *a_vp;
1113                                  * struct uio *a_uio;
1114                                  * struct ucred *a_cred;
1115                                  * } */ *ap;
1116 {
1117     int error;
1118 /*    printf("readlink %x\n", ap->a_vp);*/
1119     AFS_GLOCK();
1120     error = afs_readlink(VTOAFS(ap->a_vp), ap->a_uio, ap->a_cred);
1121     AFS_GUNLOCK();
1122     return error;
1123 }
1124
1125 static int
1126 afs_vop_inactive(ap)
1127      struct vop_inactive_args   /* {
1128                                  * struct vnode *a_vp;
1129                                  * struct thread *td;
1130                                  * } */ *ap;
1131 {
1132     struct vnode *vp = ap->a_vp;
1133
1134     AFS_GLOCK();
1135     afs_InactiveVCache(VTOAFS(vp), 0);  /* decrs ref counts */
1136     AFS_GUNLOCK();
1137     return 0;
1138 }
1139
1140 /*
1141  * struct vop_reclaim_args {
1142  *      struct vnode *a_vp;
1143  * };
1144  */
1145 static int
1146 afs_vop_reclaim(struct vop_reclaim_args *ap)
1147 {
1148     int code, slept;
1149     struct vnode *vp = ap->a_vp;
1150     struct vcache *avc = VTOAFS(vp);
1151     int haveGlock = ISAFS_GLOCK();
1152
1153     /*
1154      * In other code paths, we acquire the vnode lock while afs_xvcache is
1155      * already held (e.g. afs_PutVCache() -> vrele()). Here, we already have
1156      * the vnode lock, and we need afs_xvcache. So drop the vnode lock in order
1157      * to hold afs_xvcache.
1158      */
1159     VOP_UNLOCK(vp, 0);
1160
1161     if (!haveGlock)
1162         AFS_GLOCK();
1163     ObtainWriteLock(&afs_xvcache, 901);
1164
1165     /*
1166      * Note that we deliberately call VOP_LOCK() instead of vn_lock() here.
1167      * vn_lock() will return an error for VI_DOOMED vnodes, but we know this
1168      * vnode is already VI_DOOMED. We just want to lock it again, and skip the
1169      * VI_DOOMED check.
1170      */
1171     VOP_LOCK(vp, LK_EXCLUSIVE);
1172
1173     code = afs_FlushVCache(avc, &slept);
1174
1175     if (avc->f.states & CVInit) {
1176         avc->f.states &= ~CVInit;
1177         afs_osi_Wakeup(&avc->f.states);
1178     }
1179
1180     ReleaseWriteLock(&afs_xvcache);
1181     if (!haveGlock)
1182         AFS_GUNLOCK();
1183
1184     if (code) {
1185         afs_warn("afs_vop_reclaim: afs_FlushVCache failed code %d vnode\n", code);
1186         VOP_PRINT(vp);
1187         panic("afs: afs_FlushVCache failed during reclaim");
1188     }
1189
1190     vnode_destroy_vobject(vp);
1191     vp->v_data = 0;
1192
1193     return 0;
1194 }
1195
1196 static int
1197 afs_vop_strategy(ap)
1198      struct vop_strategy_args   /* {
1199                                  * struct buf *a_bp;
1200                                  * } */ *ap;
1201 {
1202     int error;
1203     AFS_GLOCK();
1204     error = afs_ustrategy(ap->a_bp, osi_curcred());
1205     AFS_GUNLOCK();
1206     return error;
1207 }
1208
1209 static int
1210 afs_vop_print(ap)
1211      struct vop_print_args      /* {
1212                                  * struct vnode *a_vp;
1213                                  * } */ *ap;
1214 {
1215     struct vnode *vp = ap->a_vp;
1216     struct vcache *vc = VTOAFS(ap->a_vp);
1217     int s = vc->f.states;
1218
1219     printf("vc %p vp %p tag %s, fid: %d.%d.%d.%d, opens %d, writers %d", vc, vp, vp->v_tag,
1220            (int)vc->f.fid.Cell, (u_int) vc->f.fid.Fid.Volume,
1221            (u_int) vc->f.fid.Fid.Vnode, (u_int) vc->f.fid.Fid.Unique, vc->opens,
1222            vc->execsOrWriters);
1223     printf("\n  states%s%s%s%s%s", (s & CStatd) ? " statd" : "",
1224            (s & CRO) ? " readonly" : "", (s & CDirty) ? " dirty" : "",
1225            (s & CMAPPED) ? " mapped" : "",
1226            (s & CVFlushed) ? " flush in progress" : "");
1227     printf("\n");
1228     return 0;
1229 }
1230
1231 /*
1232  * Advisory record locking support (fcntl() POSIX style)
1233  */
1234 static int
1235 afs_vop_advlock(ap)
1236      struct vop_advlock_args    /* {
1237                                  * struct vnode *a_vp;
1238                                  * caddr_t  a_id;
1239                                  * int  a_op;
1240                                  * struct flock *a_fl;
1241                                  * int  a_flags;
1242                                  * } */ *ap;
1243 {
1244     int error, a_op;
1245     struct ucred cr = *osi_curcred();
1246
1247     a_op = ap->a_op;
1248     if (a_op == F_UNLCK) {
1249         /*
1250          * When a_fl->type is F_UNLCK, FreeBSD passes in an a_op of F_UNLCK.
1251          * This is (confusingly) different than how you actually release a lock
1252          * with fcntl(), which is done with an a_op of F_SETLK and an l_type of
1253          * F_UNLCK. Pretend we were given an a_op of F_SETLK in this case,
1254          * since this is what afs_lockctl expects.
1255          */
1256         a_op = F_SETLK;
1257     }
1258
1259     AFS_GLOCK();
1260     error =
1261         afs_lockctl(VTOAFS(ap->a_vp),
1262                 ap->a_fl,
1263                 a_op, &cr,
1264                 (int)(intptr_t)ap->a_id);       /* XXX: no longer unique! */
1265     AFS_GUNLOCK();
1266     return error;
1267 }
1268
1269 struct vop_vector afs_vnodeops = {
1270         .vop_default =          &default_vnodeops,
1271         .vop_access =           afs_vop_access,
1272         .vop_advlock =          afs_vop_advlock,
1273         .vop_close =            afs_vop_close,
1274         .vop_create =           afs_vop_create,
1275         .vop_fsync =            afs_vop_fsync,
1276         .vop_getattr =          afs_vop_getattr,
1277         .vop_getpages =         afs_vop_getpages,
1278         .vop_inactive =         afs_vop_inactive,
1279         .vop_ioctl =            afs_vop_ioctl,
1280         .vop_link =             afs_vop_link,
1281         .vop_lookup =           afs_vop_lookup,
1282         .vop_mkdir =            afs_vop_mkdir,
1283         .vop_mknod =            afs_vop_mknod,
1284         .vop_open =             afs_vop_open,
1285         .vop_pathconf =         afs_vop_pathconf,
1286         .vop_print =            afs_vop_print,
1287         .vop_putpages =         afs_vop_putpages,
1288         .vop_read =             afs_vop_read,
1289         .vop_readdir =          afs_vop_readdir,
1290         .vop_readlink =         afs_vop_readlink,
1291         .vop_reclaim =          afs_vop_reclaim,
1292         .vop_remove =           afs_vop_remove,
1293         .vop_rename =           afs_vop_rename,
1294         .vop_rmdir =            afs_vop_rmdir,
1295         .vop_setattr =          afs_vop_setattr,
1296         .vop_strategy =         afs_vop_strategy,
1297         .vop_symlink =          afs_vop_symlink,
1298         .vop_write =            afs_vop_write,
1299 };