]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_subr.c
xnu-1228.9.59.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_subr.c
index 535603224f504b7e13817bc507a199f781f0b75e..c5d91125ddf9186e380222993c8dbb73cf89ab74 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -162,6 +162,7 @@ __private_extern__ int unlink1(vfs_context_t, struct nameidata *, int);
 
 static void vnode_list_add(vnode_t);
 static void vnode_list_remove(vnode_t);
+static void vnode_list_remove_locked(vnode_t);
 
 static errno_t vnode_drain(vnode_t);
 static void vgone(vnode_t, int flags);
@@ -1316,9 +1317,9 @@ loop:
                 * Alias, but not in use, so flush it out.
                 */
                if ((vp->v_iocount == 1) && (vp->v_usecount == 0)) {
-                       vnode_reclaim_internal(vp, 1, 0, 0);
+                       vnode_reclaim_internal(vp, 1, 1, 0);
+                       vnode_put_locked(vp);
                        vnode_unlock(vp);
-                       vnode_put(vp);
                        goto loop;
                }
        }
@@ -1340,8 +1341,8 @@ retnullvp:
                if (vp != NULLVP) {
                        nvp->v_flag |= VALIASED;
                        vp->v_flag |= VALIASED;
+                       vnode_put_locked(vp);
                        vnode_unlock(vp);
-                       vnode_put(vp);
                }
                return (NULLVP);
        }
@@ -1523,8 +1524,33 @@ vnode_list_add(vnode_t vp)
        vnode_list_unlock();
 }
 
+
+/*
+ * remove the vnode from appropriate free list.
+ * called with vnode LOCKED and
+ * the list lock held
+ */
+static void
+vnode_list_remove_locked(vnode_t vp)
+{
+       if (VONLIST(vp)) {
+               /*
+                * the v_listflag field is
+                * protected by the vnode_list_lock
+                */
+               if (vp->v_listflag & VLIST_RAGE)
+                       VREMRAGE("vnode_list_remove", vp);
+               else if (vp->v_listflag & VLIST_DEAD)
+                       VREMDEAD("vnode_list_remove", vp);
+               else
+                       VREMFREE("vnode_list_remove", vp);
+       }
+}
+
+
 /*
  * remove the vnode from appropriate free list.
+ * called with vnode LOCKED
  */
 static void
 vnode_list_remove(vnode_t vp)
@@ -1548,24 +1574,16 @@ vnode_list_remove(vnode_t vp)
                /*
                 * however, we're not guaranteed that
                 * we won't go from the on-list state
-                * to the non-on-list state until we
+                * to the not-on-list state until we
                 * hold the vnode_list_lock... this 
-                * is due to new_vnode removing vnodes
+                * is due to "new_vnode" removing vnodes
                 * from the free list uder the list_lock
                 * w/o the vnode lock... so we need to
                 * check again whether we're currently
                 * on the free list
                 */
-               if (VONLIST(vp)) {
-                       if (vp->v_listflag & VLIST_RAGE)
-                               VREMRAGE("vnode_list_remove", vp);
-                       else if (vp->v_listflag & VLIST_DEAD)
-                               VREMDEAD("vnode_list_remove", vp);
-                       else
-                               VREMFREE("vnode_list_remove", vp);
+               vnode_list_remove_locked(vp);
 
-                       VLISTNONE(vp);
-               }
                vnode_list_unlock();
        }
 }
@@ -1675,7 +1693,7 @@ vnode_rele_internal(vnode_t vp, int fmode, int dont_reenter, int locked)
                        goto defer_reclaim;
                }
                vnode_lock_convert(vp);
-               vnode_reclaim_internal(vp, 1, 0, 0);
+               vnode_reclaim_internal(vp, 1, 1, 0);
        }
        vnode_dropiocount(vp);
        vnode_list_add(vp);
@@ -1799,11 +1817,11 @@ loop:
 #ifdef JOE_DEBUG
                        record_vp(vp, 1);
 #endif
-                       vnode_reclaim_internal(vp, 1, 0, 0);
+                       vnode_reclaim_internal(vp, 1, 1, 0);
                        vnode_dropiocount(vp);
                        vnode_list_add(vp);
-
                        vnode_unlock(vp);
+
                        reclaimed++;
                        mount_lock(mp);
                        continue;
@@ -1819,7 +1837,7 @@ loop:
 #ifdef JOE_DEBUG
                                record_vp(vp, 1);
 #endif
-                               vnode_reclaim_internal(vp, 1, 0, 0);
+                               vnode_reclaim_internal(vp, 1, 1, 0);
                                vnode_dropiocount(vp);
                                vnode_list_add(vp);
                                vnode_unlock(vp);
@@ -1878,6 +1896,10 @@ vclean(vnode_t vp, int flags)
        int already_terminating;
        int clflags = 0;
 
+#if NAMEDSTREAMS
+       int is_namedstream;
+#endif
+
        /*
         * Check to see if the vnode is in use.
         * If so we have to reference it before we clean it out
@@ -1908,6 +1930,10 @@ vclean(vnode_t vp, int flags)
         */
        insmntque(vp, (struct mount *)0);
 
+#if NAMEDSTREAMS
+       is_namedstream = vnode_isnamedstream(vp);
+#endif
+
        vnode_unlock(vp);
 
        OSAddAtomic(1, &num_recycledvnodes);
@@ -1946,6 +1972,15 @@ vclean(vnode_t vp, int flags)
        if (active || need_inactive) 
                VNOP_INACTIVE(vp, ctx);
 
+#if NAMEDSTREAMS
+       /* Delete the shadow stream file before we reclaim its vnode */
+       if ((is_namedstream != 0) &&
+                       (vp->v_parent != NULLVP) &&
+                       ((vp->v_parent->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0)) {
+               vnode_relenamedstream(vp->v_parent, vp, ctx);
+       }
+#endif
+
        /*
         * Destroy ubc named reference
         * cluster_release is done on this path
@@ -2029,7 +2064,7 @@ vn_revoke(vnode_t vp, __unused int flags, __unused vfs_context_t a_context)
                                        SPECHASH_LOCK();        
                                        break;
                                }
-                               vnode_reclaim_internal(vq, 0, 0, 0);
+                               vnode_reclaim_internal(vq, 0, 1, 0);
                                vnode_put(vq);
                                SPECHASH_LOCK();
                                break;
@@ -2057,6 +2092,7 @@ vnode_recycle(struct vnode *vp)
                return(0);
        } 
        vnode_reclaim_internal(vp, 1, 0, 0);
+
        vnode_unlock(vp);
 
        return (1);
@@ -2209,9 +2245,9 @@ loop:
                                /*
                                 * Alias, but not in use, so flush it out.
                                 */
-                               vnode_reclaim_internal(vq, 1, 0, 0);
+                               vnode_reclaim_internal(vq, 1, 1, 0);
+                               vnode_put_locked(vq);
                                vnode_unlock(vq);
-                               vnode_put(vq);
                                goto loop;
                        }
                        count += (vq->v_usecount - vq->v_kusecount);
@@ -3229,7 +3265,6 @@ new_vnode(vnode_t *vpp)
         struct timeval current_tv;
         struct unsafe_fsnode *l_unsafefs = 0;
        proc_t  curproc = current_proc();
-       pid_t current_pid = proc_pid(curproc);
 
 retry:
        microuptime(&current_tv);
@@ -3265,6 +3300,7 @@ retry:
                mac_vnode_label_init(vp);
 #endif /* MAC */
 
+               vp->v_iocount = 1;
                goto done;
        }
 
@@ -3278,11 +3314,11 @@ retry:
                    if ( !(vp->v_listflag & VLIST_RAGE) || !(vp->v_flag & VRAGE))
                        panic("new_vnode: vp on RAGE list not marked both VLIST_RAGE and VRAGE");
 
-                   // skip vnodes which have a dependency on this process
-                   // (i.e. they're vnodes in a disk image and this process
-                   // is diskimages-helper)
+                   // if we're a dependency-capable process, skip vnodes that can
+                   // cause recycling deadlocks. (i.e. this process is diskimages
+                   // helper and the vnode is in a disk image).
                    //
-                   if (vp->v_mount && vp->v_mount->mnt_dependent_pid != current_pid && vp->v_mount->mnt_dependent_process != curproc) {
+                   if ((curproc->p_flag & P_DEPENDENCY_CAPABLE) == 0 || vp->v_mount == NULL || vp->v_mount->mnt_dependent_process == NULL) {
                        break;
                    }
 
@@ -3302,11 +3338,11 @@ retry:
                 */
                walk_count = 0;
                TAILQ_FOREACH(vp, &vnode_free_list, v_freelist) {
-                   // skip vnodes which have a dependency on this process
-                   // (i.e. they're vnodes in a disk image and this process
-                   // is diskimages-helper)
+                   // if we're a dependency-capable process, skip vnodes that can
+                   // cause recycling deadlocks. (i.e. this process is diskimages
+                   // helper and the vnode is in a disk image)
                    //
-                   if (vp->v_mount && vp->v_mount->mnt_dependent_pid != current_pid && vp->v_mount->mnt_dependent_process != curproc) {
+                   if ((curproc->p_flag & P_DEPENDENCY_CAPABLE) == 0 || vp->v_mount == NULL || vp->v_mount->mnt_dependent_process == NULL) {
                        break;
                    }
 
@@ -3353,23 +3389,23 @@ retry:
                log(LOG_EMERG, "%d desired, %d numvnodes, "
                        "%d free, %d dead, %d rage\n",
                        desiredvnodes, numvnodes, freevnodes, deadvnodes, ragevnodes);
+#if CONFIG_EMBEDDED
+               /*
+                * Running out of vnodes tends to make a system unusable.  On an
+                * embedded system, it's unlikely that the user can do anything
+                * about it (or would know what to do, if they could).  So panic
+                * the system so it will automatically restart (and hopefully we
+                * can get a panic log that tells us why we ran out).
+                */
+               panic("vnode table is full\n");
+#endif
                *vpp = NULL;
                return (ENFILE);
        }
 steal_this_vp:
        vpid = vp->v_id;
 
-       /*
-        * the v_listflag field is
-        * protected by the vnode_list_lock
-        */
-       if (vp->v_listflag & VLIST_DEAD)
-               VREMDEAD("new_vnode", vp);
-       else if (vp->v_listflag & VLIST_RAGE)
-               VREMRAGE("new_vnode", vp);
-       else
-               VREMFREE("new_vnode", vp);
-       VLISTNONE(vp);
+       vnode_list_remove_locked(vp);
 
        vnode_list_unlock();
        vnode_lock_spin(vp);
@@ -3421,7 +3457,6 @@ steal_this_vp:
                if (vp->v_lflag & VL_DEAD)
                        panic("new_vnode: the vnode is VL_DEAD but not VBAD");
                vnode_lock_convert(vp);
-
                (void)vnode_reclaim_internal(vp, 1, 1, 0);
 
                if ((VONLIST(vp)))
@@ -3452,6 +3487,7 @@ steal_this_vp:
        }
 #endif /* MAC */
 
+       vp->v_iocount = 1;
        vp->v_lflag = 0;
        vp->v_writecount = 0;
         vp->v_references = 0;
@@ -3532,6 +3568,12 @@ vnode_getwithref(vnode_t vp)
 }
 
 
+__private_extern__ int
+vnode_getalways(vnode_t vp)
+{
+        return(vget_internal(vp, 0, VNODE_ALWAYS));
+}
+
 int
 vnode_put(vnode_t vp)
 {
@@ -3580,7 +3622,7 @@ retry:
 
        if ((vp->v_lflag & (VL_MARKTERM | VL_TERMINATE | VL_DEAD)) == VL_MARKTERM) {
                vnode_lock_convert(vp);
-               vnode_reclaim_internal(vp, 1, 0, 0);
+               vnode_reclaim_internal(vp, 1, 1, 0);
        }
        vnode_dropiocount(vp);
        vnode_list_add(vp);
@@ -3700,6 +3742,7 @@ vnode_getiocount(vnode_t vp, int vid, int vflags)
 {
        int nodead = vflags & VNODE_NODEAD;
        int nosusp = vflags & VNODE_NOSUSPEND;
+       int always = vflags & VNODE_ALWAYS;
 
        for (;;) {
                /*
@@ -3728,6 +3771,8 @@ vnode_getiocount(vnode_t vp, int vid, int vflags)
                    (vp->v_owner == current_thread())) {
                        break;
                }
+               if (always != 0) 
+                       break;
                vnode_lock_convert(vp);
 
                if (vp->v_lflag & VL_TERMINATE) {
@@ -3808,9 +3853,19 @@ vnode_reclaim_internal(struct vnode * vp, int locked, int reuse, int flags)
         * once new_vnode drops the list_lock, it will block trying to take
         * the vnode lock until we release it... at that point it will evaluate
         * whether the v_vid has changed
+        * also need to make sure that the vnode isn't on a list where "new_vnode"
+        * can find it after the v_id has been bumped until we are completely done
+        * with the vnode (i.e. putting it back on a list has to be the very last
+        * thing we do to this vnode... many of the callers of vnode_reclaim_internal
+        * are holding an io_count on the vnode... they need to drop the io_count
+        * BEFORE doing a vnode_list_add or make sure to hold the vnode lock until
+        * they are completely done with the vnode
         */
        vnode_list_lock();
+
+       vnode_list_remove_locked(vp);
        vp->v_id++;
+
        vnode_list_unlock();
 
        if (isfifo) {
@@ -3826,7 +3881,7 @@ vnode_reclaim_internal(struct vnode * vp, int locked, int reuse, int flags)
        if (vp->v_data)
                panic("vnode_reclaim_internal: cleaned vnode isn't");
        if (vp->v_numoutput)
-               panic("vnode_reclaim_internal: Clean vnode has pending I/O's");
+               panic("vnode_reclaim_internal: clean vnode has pending I/O's");
        if (UBCINFOEXISTS(vp))
                panic("vnode_reclaim_internal: ubcinfo not cleaned");
        if (vp->v_parent)
@@ -3844,12 +3899,11 @@ vnode_reclaim_internal(struct vnode * vp, int locked, int reuse, int flags)
                vp->v_lflag &= ~VL_TERMWANT;
                wakeup(&vp->v_lflag);
        }
-       if (!reuse && vp->v_usecount == 0) {
+       if (!reuse) {
                /*
                 * make sure we get on the
-                * dead list
+                * dead list if appropriate
                 */
-               vnode_list_remove(vp);
                vnode_list_add(vp);
        }
        if (!locked)
@@ -3857,9 +3911,6 @@ vnode_reclaim_internal(struct vnode * vp, int locked, int reuse, int flags)
 }
 
 /* USAGE:
- * The following api creates a vnode and associates all the parameter specified in vnode_fsparam
- * structure and returns a vnode handle with a reference. device aliasing is handled here so checkalias
- * is obsoleted by this.
  *  vnode_create(int flavor, size_t size, void * param,  vnode_t  *vp)
  */
 int  
@@ -3884,7 +3935,6 @@ vnode_create(int flavor, size_t size, void *data, vnode_t *vpp)
                        vp->v_op = param->vnfs_vops;
                        vp->v_type = param->vnfs_vtype;
                        vp->v_data = param->vnfs_fsnode;
-                       vp->v_iocount = 1;
 
                        if (param->vnfs_markroot)
                                vp->v_flag |= VROOT;
@@ -6532,6 +6582,22 @@ errno_t rmdir_remove_orphaned_appleDouble(vnode_t vp , vfs_context_t ctx, int *
                        cpos += dp->d_reclen;
                        dp = (struct dirent*)cpos;
                }
+               
+               /*
+                * workaround for HFS/NFS setting eofflag before end of file 
+                */
+               if (vp->v_tag == VT_HFS && nentries > 2)
+                       eofflag=0;
+
+               if (vp->v_tag == VT_NFS) {
+                       if (eofflag && !full_erase_flag) {
+                               full_erase_flag = 1;
+                               eofflag = 0;
+                               uio_reset(auio, 0, UIO_SYSSPACE, UIO_READ);
+                       }
+                       else if (!eofflag && full_erase_flag)
+                               full_erase_flag = 0;
+               }
 
        } while (!eofflag);
        /*
@@ -6542,6 +6608,7 @@ errno_t rmdir_remove_orphaned_appleDouble(vnode_t vp , vfs_context_t ctx, int *
 
        uio_reset(auio, 0, UIO_SYSSPACE, UIO_READ);
        eofflag = 0;
+       full_erase_flag = 0;
 
        do {
                siz = UIO_BUFF_SIZE;