]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/dcache.c
getxattr: use correct xattr length
[mirror_ubuntu-bionic-kernel.git] / fs / dcache.c
index 34c852af215c0f1ff2d73800ed0bc02c9a2f0794..ca3129bfe1089a7d71cded900fc04f6354d2fcaf 100644 (file)
@@ -468,9 +468,11 @@ static void dentry_lru_add(struct dentry *dentry)
  * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
  * reason (NFS timeouts or autofs deletes).
  *
- * __d_drop requires dentry->d_lock.
+ * __d_drop requires dentry->d_lock
+ * ___d_drop doesn't mark dentry as "unhashed"
+ *   (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
  */
-void __d_drop(struct dentry *dentry)
+static void ___d_drop(struct dentry *dentry)
 {
        if (!d_unhashed(dentry)) {
                struct hlist_bl_head *b;
@@ -486,12 +488,17 @@ void __d_drop(struct dentry *dentry)
 
                hlist_bl_lock(b);
                __hlist_bl_del(&dentry->d_hash);
-               dentry->d_hash.pprev = NULL;
                hlist_bl_unlock(b);
                /* After this call, in-progress rcu-walk path lookup will fail. */
                write_seqcount_invalidate(&dentry->d_seq);
        }
 }
+
+void __d_drop(struct dentry *dentry)
+{
+       ___d_drop(dentry);
+       dentry->d_hash.pprev = NULL;
+}
 EXPORT_SYMBOL(__d_drop);
 
 void d_drop(struct dentry *dentry)
@@ -630,7 +637,7 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
        rcu_read_lock();
        spin_unlock(&dentry->d_lock);
 again:
-       parent = ACCESS_ONCE(dentry->d_parent);
+       parent = READ_ONCE(dentry->d_parent);
        spin_lock(&parent->d_lock);
        /*
         * We can't blindly lock dentry until we are sure
@@ -644,11 +651,16 @@ again:
                spin_unlock(&parent->d_lock);
                goto again;
        }
-       rcu_read_unlock();
-       if (parent != dentry)
+       if (parent != dentry) {
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
-       else
+               if (unlikely(dentry->d_lockref.count < 0)) {
+                       spin_unlock(&parent->d_lock);
+                       parent = NULL;
+               }
+       } else {
                parent = NULL;
+       }
+       rcu_read_unlock();
        return parent;
 }
 
@@ -721,7 +733,7 @@ static inline bool fast_dput(struct dentry *dentry)
         * around with a zero refcount.
         */
        smp_rmb();
-       d_flags = ACCESS_ONCE(dentry->d_flags);
+       d_flags = READ_ONCE(dentry->d_flags);
        d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST | DCACHE_DISCONNECTED;
 
        /* Nothing to do? Dropping the reference was all we needed? */
@@ -850,11 +862,11 @@ struct dentry *dget_parent(struct dentry *dentry)
         * locking.
         */
        rcu_read_lock();
-       ret = ACCESS_ONCE(dentry->d_parent);
+       ret = READ_ONCE(dentry->d_parent);
        gotref = lockref_get_not_zero(&ret->d_lockref);
        rcu_read_unlock();
        if (likely(gotref)) {
-               if (likely(ret == ACCESS_ONCE(dentry->d_parent)))
+               if (likely(ret == READ_ONCE(dentry->d_parent)))
                        return ret;
                dput(ret);
        }
@@ -1197,7 +1209,7 @@ enum d_walk_ret {
  *
  * The @enter() and @finish() callbacks are called with d_lock held.
  */
-static void d_walk(struct dentry *parent, void *data,
+void d_walk(struct dentry *parent, void *data,
                   enum d_walk_ret (*enter)(void *, struct dentry *),
                   void (*finish)(void *))
 {
@@ -1305,6 +1317,7 @@ rename_retry:
        seq = 1;
        goto again;
 }
+EXPORT_SYMBOL_GPL(d_walk);
 
 struct check_mount {
        struct vfsmount *mnt;
@@ -1855,6 +1868,28 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
 }
 EXPORT_SYMBOL(d_instantiate);
 
+/*
+ * This should be equivalent to d_instantiate() + unlock_new_inode(),
+ * with lockdep-related part of unlock_new_inode() done before
+ * anything else.  Use that instead of open-coding d_instantiate()/
+ * unlock_new_inode() combinations.
+ */
+void d_instantiate_new(struct dentry *entry, struct inode *inode)
+{
+       BUG_ON(!hlist_unhashed(&entry->d_u.d_alias));
+       BUG_ON(!inode);
+       lockdep_annotate_inode_mutex_key(inode);
+       security_d_instantiate(entry, inode);
+       spin_lock(&inode->i_lock);
+       __d_instantiate(entry, inode);
+       WARN_ON(!(inode->i_state & I_NEW));
+       inode->i_state &= ~I_NEW;
+       smp_mb();
+       wake_up_bit(&inode->i_state, __I_NEW);
+       spin_unlock(&inode->i_lock);
+}
+EXPORT_SYMBOL(d_instantiate_new);
+
 /**
  * d_instantiate_no_diralias - instantiate a non-aliased dentry
  * @entry: dentry to complete
@@ -2381,7 +2416,7 @@ EXPORT_SYMBOL(d_delete);
 static void __d_rehash(struct dentry *entry)
 {
        struct hlist_bl_head *b = d_hash(entry->d_name.hash);
-       BUG_ON(!d_unhashed(entry));
+
        hlist_bl_lock(b);
        hlist_bl_add_head_rcu(&entry->d_hash, b);
        hlist_bl_unlock(b);
@@ -2448,7 +2483,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
 
 retry:
        rcu_read_lock();
-       seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
+       seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
        r_seq = read_seqbegin(&rename_lock);
        dentry = __d_lookup_rcu(parent, name, &d_seq);
        if (unlikely(dentry)) {
@@ -2469,8 +2504,14 @@ retry:
                rcu_read_unlock();
                goto retry;
        }
+
+       if (unlikely(seq & 1)) {
+               rcu_read_unlock();
+               goto retry;
+       }
+
        hlist_bl_lock(b);
-       if (unlikely(parent->d_inode->i_dir_seq != seq)) {
+       if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {
                hlist_bl_unlock(b);
                rcu_read_unlock();
                goto retry;
@@ -2705,8 +2746,6 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
                         */
                        unsigned int i;
                        BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
-                       kmemcheck_mark_initialized(dentry->d_iname, DNAME_INLINE_LEN);
-                       kmemcheck_mark_initialized(target->d_iname, DNAME_INLINE_LEN);
                        for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
                                swap(((long *) &dentry->d_iname)[i],
                                     ((long *) &target->d_iname)[i]);
@@ -2818,9 +2857,9 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
        write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED);
 
        /* unhash both */
-       /* __d_drop does write_seqcount_barrier, but they're OK to nest. */
-       __d_drop(dentry);
-       __d_drop(target);
+       /* ___d_drop does write_seqcount_barrier, but they're OK to nest. */
+       ___d_drop(dentry);
+       ___d_drop(target);
 
        /* Switch the names.. */
        if (exchange)
@@ -2832,6 +2871,8 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
        __d_rehash(dentry);
        if (exchange)
                __d_rehash(target);
+       else
+               target->d_hash.pprev = NULL;
 
        /* ... and switch them in the tree */
        if (IS_ROOT(dentry)) {
@@ -2894,6 +2935,7 @@ void d_exchange(struct dentry *dentry1, struct dentry *dentry2)
 
        write_sequnlock(&rename_lock);
 }
+EXPORT_SYMBOL_GPL(d_exchange);
 
 /**
  * d_ancestor - search for an ancestor
@@ -3040,7 +3082,7 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen)
  * @buflen: allocated length of the buffer
  * @name:   name string and length qstr structure
  *
- * With RCU path tracing, it may race with d_move(). Use ACCESS_ONCE() to
+ * With RCU path tracing, it may race with d_move(). Use READ_ONCE() to
  * make sure that either the old or the new name pointer and length are
  * fetched. However, there may be mismatch between length and pointer.
  * The length cannot be trusted, we need to copy it byte-by-byte until
@@ -3054,8 +3096,8 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen)
  */
 static int prepend_name(char **buffer, int *buflen, const struct qstr *name)
 {
-       const char *dname = ACCESS_ONCE(name->name);
-       u32 dlen = ACCESS_ONCE(name->len);
+       const char *dname = READ_ONCE(name->name);
+       u32 dlen = READ_ONCE(name->len);
        char *p;
 
        smp_read_barrier_depends();
@@ -3120,7 +3162,7 @@ restart:
                struct dentry * parent;
 
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
-                       struct mount *parent = ACCESS_ONCE(mnt->mnt_parent);
+                       struct mount *parent = READ_ONCE(mnt->mnt_parent);
                        /* Escaped? */
                        if (dentry != vfsmnt->mnt_root) {
                                bptr = *buffer;
@@ -3130,7 +3172,7 @@ restart:
                        }
                        /* Global root? */
                        if (mnt != parent) {
-                               dentry = ACCESS_ONCE(mnt->mnt_mountpoint);
+                               dentry = READ_ONCE(mnt->mnt_mountpoint);
                                mnt = parent;
                                vfsmnt = &mnt->mnt;
                                continue;