]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/dcache.c
kprobes/x86: Use preempt_enable() in optimized_callback()
[mirror_ubuntu-bionic-kernel.git] / fs / dcache.c
index 5c7df1df81ff8094dd4de8543653ff092433d13a..082353d1390eadd174b5bef5226663dc1e5b773e 100644 (file)
@@ -270,11 +270,25 @@ static void __d_free(struct rcu_head *head)
        kmem_cache_free(dentry_cache, dentry); 
 }
 
+static void __d_free_external_name(struct rcu_head *head)
+{
+       struct external_name *name = container_of(head, struct external_name,
+                                                 u.head);
+
+       mod_node_page_state(page_pgdat(virt_to_page(name)),
+                           NR_INDIRECTLY_RECLAIMABLE_BYTES,
+                           -ksize(name));
+
+       kfree(name);
+}
+
 static void __d_free_external(struct rcu_head *head)
 {
        struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu);
-       kfree(external_name(dentry));
-       kmem_cache_free(dentry_cache, dentry); 
+
+       __d_free_external_name(&external_name(dentry)->u.head);
+
+       kmem_cache_free(dentry_cache, dentry);
 }
 
 static inline int dname_external(const struct dentry *dentry)
@@ -291,7 +305,8 @@ void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry
                spin_unlock(&dentry->d_lock);
                name->name = p->name;
        } else {
-               memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
+               memcpy(name->inline_name, dentry->d_iname,
+                      dentry->d_name.len + 1);
                spin_unlock(&dentry->d_lock);
                name->name = name->inline_name;
        }
@@ -304,7 +319,7 @@ void release_dentry_name_snapshot(struct name_snapshot *name)
                struct external_name *p;
                p = container_of(name->name, struct external_name, name[0]);
                if (unlikely(atomic_dec_and_test(&p->u.count)))
-                       kfree_rcu(p, u.head);
+                       call_rcu(&p->u.head, __d_free_external_name);
        }
 }
 EXPORT_SYMBOL(release_dentry_name_snapshot);
@@ -357,14 +372,11 @@ static void dentry_unlink_inode(struct dentry * dentry)
        __releases(dentry->d_inode->i_lock)
 {
        struct inode *inode = dentry->d_inode;
-       bool hashed = !d_unhashed(dentry);
 
-       if (hashed)
-               raw_write_seqcount_begin(&dentry->d_seq);
+       raw_write_seqcount_begin(&dentry->d_seq);
        __d_clear_type_and_inode(dentry);
        hlist_del_init(&dentry->d_u.d_alias);
-       if (hashed)
-               raw_write_seqcount_end(&dentry->d_seq);
+       raw_write_seqcount_end(&dentry->d_seq);
        spin_unlock(&dentry->d_lock);
        spin_unlock(&inode->i_lock);
        if (!inode->i_nlink)
@@ -468,9 +480,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 +500,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)
@@ -644,11 +663,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;
 }
 
@@ -1197,7 +1221,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 +1329,7 @@ rename_retry:
        seq = 1;
        goto again;
 }
+EXPORT_SYMBOL_GPL(d_walk);
 
 struct check_mount {
        struct vfsmount *mnt;
@@ -1595,6 +1620,7 @@ EXPORT_SYMBOL(d_invalidate);
  
 struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
 {
+       struct external_name *ext = NULL;
        struct dentry *dentry;
        char *dname;
        int err;
@@ -1615,14 +1641,13 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
                dname = dentry->d_iname;
        } else if (name->len > DNAME_INLINE_LEN-1) {
                size_t size = offsetof(struct external_name, name[1]);
-               struct external_name *p = kmalloc(size + name->len,
-                                                 GFP_KERNEL_ACCOUNT);
-               if (!p) {
+               ext = kmalloc(size + name->len, GFP_KERNEL_ACCOUNT);
+               if (!ext) {
                        kmem_cache_free(dentry_cache, dentry); 
                        return NULL;
                }
-               atomic_set(&p->u.count, 1);
-               dname = p->name;
+               atomic_set(&ext->u.count, 1);
+               dname = ext->name;
                if (IS_ENABLED(CONFIG_DCACHE_WORD_ACCESS))
                        kasan_unpoison_shadow(dname,
                                round_up(name->len + 1, sizeof(unsigned long)));
@@ -1665,6 +1690,12 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
                }
        }
 
+       if (unlikely(ext)) {
+               pg_data_t *pgdat = page_pgdat(virt_to_page(ext));
+               mod_node_page_state(pgdat, NR_INDIRECTLY_RECLAIMABLE_BYTES,
+                                   ksize(ext));
+       }
+
        this_cpu_inc(nr_dentry);
 
        return dentry;
@@ -1855,6 +1886,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 & ~I_CREATING;
+       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
@@ -1888,10 +1941,12 @@ struct dentry *d_make_root(struct inode *root_inode)
 
        if (root_inode) {
                res = __d_alloc(root_inode->i_sb, NULL);
-               if (res)
+               if (res) {
+                       res->d_flags |= DCACHE_RCUACCESS;
                        d_instantiate(res, root_inode);
-               else
+               } else {
                        iput(root_inode);
+               }
        }
        return res;
 }
@@ -2381,7 +2436,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 +2503,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 +2524,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;
@@ -2729,7 +2790,7 @@ static void copy_name(struct dentry *dentry, struct dentry *target)
                dentry->d_name.hash_len = target->d_name.hash_len;
        }
        if (old_name && likely(atomic_dec_and_test(&old_name->u.count)))
-               kfree_rcu(old_name, u.head);
+               call_rcu(&old_name->u.head, __d_free_external_name);
 }
 
 static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target)
@@ -2816,9 +2877,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)
@@ -2830,6 +2891,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)) {
@@ -2892,6 +2955,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