Re: [PATCH v3 03/21] dentry: switch the lists of children to hlist

From: Amir Goldstein
Date: Fri Nov 24 2023 - 02:44:52 EST


On Fri, Nov 24, 2023 at 8:05 AM Al Viro <viro@xxxxxxxxxxxxxxxxxx> wrote:
>
> Saves a pointer per struct dentry and actually makes the things less
> clumsy. Cleaned the d_walk() and dcache_readdir() a bit by use
> of hlist_for_... iterators.
>
> A couple of new helpers - d_first_child() and d_next_sibling(),
> to make the expressions less awful.
>
> X-fuck-kABI: gladly

???

> Reviewed-by: Christian Brauner <brauner@xxxxxxxxxx>
> Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
> ---
> Documentation/filesystems/porting.rst | 9 +++
> arch/powerpc/platforms/cell/spufs/inode.c | 5 +-
> fs/afs/dynroot.c | 5 +-
> fs/autofs/expire.c | 7 +--
> fs/ceph/dir.c | 2 +-
> fs/ceph/mds_client.c | 2 +-
> fs/coda/cache.c | 2 +-
> fs/dcache.c | 76 +++++++++++------------
> fs/libfs.c | 45 +++++++-------
> fs/notify/fsnotify.c | 2 +-
> fs/tracefs/inode.c | 34 +++++-----
> include/linux/dcache.h | 20 ++++--
> 12 files changed, 108 insertions(+), 101 deletions(-)
>
> diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
> index 878e72b2f8b7..331405f4b29f 100644
> --- a/Documentation/filesystems/porting.rst
> +++ b/Documentation/filesystems/porting.rst
> @@ -1061,3 +1061,12 @@ export_operations ->encode_fh() no longer has a default implementation to
> encode FILEID_INO32_GEN* file handles.
> Filesystems that used the default implementation may use the generic helper
> generic_encode_ino32_fh() explicitly.
> +
> +---
> +
> +**mandatory**
> +
> +The list of children anchored in parent dentry got turned into hlist now.
> +Field names got changed (->d_children/->d_sib instead of ->d_subdirs/->d_child
> +for anchor/entries resp.), so any affected places will be immediately caught
> +by compiler.
> diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
> index 10c1320adfd0..030de2b8c145 100644
> --- a/arch/powerpc/platforms/cell/spufs/inode.c
> +++ b/arch/powerpc/platforms/cell/spufs/inode.c
> @@ -145,10 +145,11 @@ spufs_evict_inode(struct inode *inode)
>
> static void spufs_prune_dir(struct dentry *dir)
> {
> - struct dentry *dentry, *tmp;
> + struct dentry *dentry;
> + struct hlist_node *n;
>
> inode_lock(d_inode(dir));
> - list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) {
> + hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) {
> spin_lock(&dentry->d_lock);
> if (simple_positive(dentry)) {
> dget_dlock(dentry);
> diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
> index 4d04ef2d3ae7..fe45462834cc 100644
> --- a/fs/afs/dynroot.c
> +++ b/fs/afs/dynroot.c
> @@ -370,7 +370,7 @@ int afs_dynroot_populate(struct super_block *sb)
> void afs_dynroot_depopulate(struct super_block *sb)
> {
> struct afs_net *net = afs_sb2net(sb);
> - struct dentry *root = sb->s_root, *subdir, *tmp;
> + struct dentry *root = sb->s_root, *subdir;
>
> /* Prevent more subdirs from being created */
> mutex_lock(&net->proc_cells_lock);
> @@ -379,10 +379,11 @@ void afs_dynroot_depopulate(struct super_block *sb)
> mutex_unlock(&net->proc_cells_lock);
>
> if (root) {
> + struct hlist_node *n;
> inode_lock(root->d_inode);
>
> /* Remove all the pins for dirs created for manually added cells */
> - list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
> + hlist_for_each_entry_safe(subdir, n, &root->d_children, d_sib) {
> if (subdir->d_fsdata) {
> subdir->d_fsdata = NULL;
> dput(subdir);
> diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
> index 038b3d2d9f57..39d8c84c16f4 100644
> --- a/fs/autofs/expire.c
> +++ b/fs/autofs/expire.c
> @@ -73,12 +73,9 @@ static int autofs_mount_busy(struct vfsmount *mnt,
> /* p->d_lock held */
> static struct dentry *positive_after(struct dentry *p, struct dentry *child)
> {
> - if (child)
> - child = list_next_entry(child, d_child);
> - else
> - child = list_first_entry(&p->d_subdirs, struct dentry, d_child);
> + child = child ? d_next_sibling(child) : d_first_child(p);
>
> - list_for_each_entry_from(child, &p->d_subdirs, d_child) {
> + hlist_for_each_entry_from(child, d_sib) {
> spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
> if (simple_positive(child)) {
> dget_dlock(child);
> diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
> index 91709934c8b1..678596684596 100644
> --- a/fs/ceph/dir.c
> +++ b/fs/ceph/dir.c
> @@ -174,7 +174,7 @@ __dcache_find_get_entry(struct dentry *parent, u64 idx,
> /*
> * When possible, we try to satisfy a readdir by peeking at the
> * dcache. We make this work by carefully ordering dentries on
> - * d_child when we initially get results back from the MDS, and
> + * d_children when we initially get results back from the MDS, and
> * falling back to a "normal" sync readdir if any dentries in the dir
> * are dropped.
> *
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index d95eb525519a..02ebfabfc8ee 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -2128,7 +2128,7 @@ static bool drop_negative_children(struct dentry *dentry)
> goto out;
>
> spin_lock(&dentry->d_lock);
> - list_for_each_entry(child, &dentry->d_subdirs, d_child) {
> + hlist_for_each_entry(child, &dentry->d_children, d_sib) {
> if (d_really_is_positive(child)) {
> all_negative = false;
> break;
> diff --git a/fs/coda/cache.c b/fs/coda/cache.c
> index bfbc03c6b632..f5b71a35f9db 100644
> --- a/fs/coda/cache.c
> +++ b/fs/coda/cache.c
> @@ -94,7 +94,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
>
> rcu_read_lock();
> spin_lock(&parent->d_lock);
> - list_for_each_entry(de, &parent->d_subdirs, d_child) {
> + hlist_for_each_entry(de, &parent->d_children, d_sib) {
> struct inode *inode = d_inode_rcu(de);
> /* don't know what to do with negative dentries */
> if (inode)
> diff --git a/fs/dcache.c b/fs/dcache.c
> index c82ae731df9a..59f76c9a15d1 100644
> --- a/fs/dcache.c
> +++ b/fs/dcache.c
> @@ -51,8 +51,8 @@
> * - d_lru
> * - d_count
> * - d_unhashed()
> - * - d_parent and d_subdirs
> - * - childrens' d_child and d_parent
> + * - d_parent and d_chilren
> + * - childrens' d_sib and d_parent
> * - d_u.d_alias, d_inode
> *
> * Ordering:
> @@ -537,7 +537,7 @@ void d_drop(struct dentry *dentry)
> }
> EXPORT_SYMBOL(d_drop);
>
> -static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
> +static inline void dentry_unlist(struct dentry *dentry)
> {
> struct dentry *next;
> /*
> @@ -545,12 +545,12 @@ static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
> * attached to the dentry tree
> */
> dentry->d_flags |= DCACHE_DENTRY_KILLED;
> - if (unlikely(list_empty(&dentry->d_child)))
> + if (unlikely(hlist_unhashed(&dentry->d_sib)))
> return;
> - __list_del_entry(&dentry->d_child);
> + __hlist_del(&dentry->d_sib);
> /*
> * Cursors can move around the list of children. While we'd been
> - * a normal list member, it didn't matter - ->d_child.next would've
> + * a normal list member, it didn't matter - ->d_sib.next would've
> * been updated. However, from now on it won't be and for the
> * things like d_walk() it might end up with a nasty surprise.
> * Normally d_walk() doesn't care about cursors moving around -
> @@ -558,20 +558,20 @@ static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
> * of its own, we get through it without ever unlocking the parent.
> * There is one exception, though - if we ascend from a child that
> * gets killed as soon as we unlock it, the next sibling is found
> - * using the value left in its ->d_child.next. And if _that_
> + * using the value left in its ->d_sib.next. And if _that_
> * pointed to a cursor, and cursor got moved (e.g. by lseek())
> * before d_walk() regains parent->d_lock, we'll end up skipping
> * everything the cursor had been moved past.
> *
> - * Solution: make sure that the pointer left behind in ->d_child.next
> + * Solution: make sure that the pointer left behind in ->d_sib.next
> * points to something that won't be moving around. I.e. skip the
> * cursors.
> */
> - while (dentry->d_child.next != &parent->d_subdirs) {
> - next = list_entry(dentry->d_child.next, struct dentry, d_child);
> + while (dentry->d_sib.next) {
> + next = hlist_entry(dentry->d_sib.next, struct dentry, d_sib);
> if (likely(!(next->d_flags & DCACHE_DENTRY_CURSOR)))
> break;
> - dentry->d_child.next = next->d_child.next;
> + dentry->d_sib.next = next->d_sib.next;
> }
> }
>
> @@ -600,7 +600,7 @@ static void __dentry_kill(struct dentry *dentry)
> }
> /* if it was on the hash then remove it */
> __d_drop(dentry);
> - dentry_unlist(dentry, parent);
> + dentry_unlist(dentry);
> if (parent)
> spin_unlock(&parent->d_lock);
> if (dentry->d_inode)
> @@ -1348,8 +1348,7 @@ enum d_walk_ret {
> static void d_walk(struct dentry *parent, void *data,
> enum d_walk_ret (*enter)(void *, struct dentry *))
> {
> - struct dentry *this_parent;
> - struct list_head *next;
> + struct dentry *this_parent, *dentry;
> unsigned seq = 0;
> enum d_walk_ret ret;
> bool retry = true;
> @@ -1371,13 +1370,9 @@ static void d_walk(struct dentry *parent, void *data,
> break;
> }
> repeat:
> - next = this_parent->d_subdirs.next;
> + dentry = d_first_child(this_parent);
> resume:
> - while (next != &this_parent->d_subdirs) {
> - struct list_head *tmp = next;
> - struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
> - next = tmp->next;
> -
> + hlist_for_each_entry_from(dentry, d_sib) {
> if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
> continue;
>
> @@ -1398,7 +1393,7 @@ static void d_walk(struct dentry *parent, void *data,
> continue;
> }
>
> - if (!list_empty(&dentry->d_subdirs)) {
> + if (!hlist_empty(&dentry->d_children)) {
> spin_unlock(&this_parent->d_lock);
> spin_release(&dentry->d_lock.dep_map, _RET_IP_);
> this_parent = dentry;
> @@ -1413,24 +1408,23 @@ static void d_walk(struct dentry *parent, void *data,
> rcu_read_lock();
> ascend:
> if (this_parent != parent) {
> - struct dentry *child = this_parent;
> - this_parent = child->d_parent;
> + dentry = this_parent;
> + this_parent = dentry->d_parent;
>
> - spin_unlock(&child->d_lock);
> + spin_unlock(&dentry->d_lock);
> spin_lock(&this_parent->d_lock);
>
> /* might go back up the wrong parent if we have had a rename. */
> if (need_seqretry(&rename_lock, seq))
> goto rename_retry;
> /* go into the first sibling still alive */
> - do {
> - next = child->d_child.next;
> - if (next == &this_parent->d_subdirs)
> - goto ascend;
> - child = list_entry(next, struct dentry, d_child);
> - } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
> - rcu_read_unlock();
> - goto resume;
> + hlist_for_each_entry_continue(dentry, d_sib) {
> + if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
> + rcu_read_unlock();
> + goto resume;
> + }
> + }
> + goto ascend;
> }
> if (need_seqretry(&rename_lock, seq))
> goto rename_retry;
> @@ -1530,7 +1524,7 @@ int d_set_mounted(struct dentry *dentry)
> * Search the dentry child list of the specified parent,
> * and move any unused dentries to the end of the unused
> * list for prune_dcache(). We descend to the next level
> - * whenever the d_subdirs list is non-empty and continue
> + * whenever the d_children list is non-empty and continue
> * searching.
> *
> * It returns zero iff there are no unused children,
> @@ -1657,7 +1651,7 @@ EXPORT_SYMBOL(shrink_dcache_parent);
> static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
> {
> /* it has busy descendents; complain about those instead */
> - if (!list_empty(&dentry->d_subdirs))
> + if (!hlist_empty(&dentry->d_children))
> return D_WALK_CONTINUE;
>
> /* root with refcount 1 is fine */
> @@ -1814,9 +1808,9 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
> dentry->d_fsdata = NULL;
> INIT_HLIST_BL_NODE(&dentry->d_hash);
> INIT_LIST_HEAD(&dentry->d_lru);
> - INIT_LIST_HEAD(&dentry->d_subdirs);
> + INIT_HLIST_HEAD(&dentry->d_children);
> INIT_HLIST_NODE(&dentry->d_u.d_alias);
> - INIT_LIST_HEAD(&dentry->d_child);
> + INIT_HLIST_NODE(&dentry->d_sib);
> d_set_d_op(dentry, dentry->d_sb->s_d_op);
>
> if (dentry->d_op && dentry->d_op->d_init) {
> @@ -1855,7 +1849,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
> */
> __dget_dlock(parent);
> dentry->d_parent = parent;
> - list_add(&dentry->d_child, &parent->d_subdirs);
> + hlist_add_head(&dentry->d_sib, &parent->d_children);
> spin_unlock(&parent->d_lock);
>
> return dentry;
> @@ -2993,11 +2987,15 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
> } else {
> target->d_parent = old_parent;
> swap_names(dentry, target);
> - list_move(&target->d_child, &target->d_parent->d_subdirs);
> + if (!hlist_unhashed(&target->d_sib))
> + __hlist_del(&target->d_sib);
> + hlist_add_head(&target->d_sib, &target->d_parent->d_children);
> __d_rehash(target);
> fsnotify_update_flags(target);
> }
> - list_move(&dentry->d_child, &dentry->d_parent->d_subdirs);
> + if (!hlist_unhashed(&dentry->d_sib))
> + __hlist_del(&dentry->d_sib);
> + hlist_add_head(&dentry->d_sib, &dentry->d_parent->d_children);
> __d_rehash(dentry);
> fsnotify_update_flags(dentry);
> fscrypt_handle_d_move(dentry);
> diff --git a/fs/libfs.c b/fs/libfs.c
> index e9440d55073c..46c9177769c1 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -104,15 +104,16 @@ EXPORT_SYMBOL(dcache_dir_close);
> * If no such element exists, NULL is returned.
> */
> static struct dentry *scan_positives(struct dentry *cursor,
> - struct list_head *p,
> + struct hlist_node **p,
> loff_t count,
> struct dentry *last)
> {
> struct dentry *dentry = cursor->d_parent, *found = NULL;
>
> spin_lock(&dentry->d_lock);
> - while ((p = p->next) != &dentry->d_subdirs) {
> - struct dentry *d = list_entry(p, struct dentry, d_child);
> + while (*p) {
> + struct dentry *d = hlist_entry(*p, struct dentry, d_sib);
> + p = &d->d_sib.next;
> // we must at least skip cursors, to avoid livelocks
> if (d->d_flags & DCACHE_DENTRY_CURSOR)
> continue;
> @@ -126,8 +127,10 @@ static struct dentry *scan_positives(struct dentry *cursor,
> count = 1;
> }
> if (need_resched()) {
> - list_move(&cursor->d_child, p);
> - p = &cursor->d_child;
> + if (!hlist_unhashed(&cursor->d_sib))
> + __hlist_del(&cursor->d_sib);
> + hlist_add_behind(&cursor->d_sib, &d->d_sib);
> + p = &cursor->d_sib.next;
> spin_unlock(&dentry->d_lock);
> cond_resched();
> spin_lock(&dentry->d_lock);
> @@ -159,13 +162,12 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
> inode_lock_shared(dentry->d_inode);
>
> if (offset > 2)
> - to = scan_positives(cursor, &dentry->d_subdirs,
> + to = scan_positives(cursor, &dentry->d_children.first,
> offset - 2, NULL);
> spin_lock(&dentry->d_lock);
> + hlist_del_init(&cursor->d_sib);
> if (to)
> - list_move(&cursor->d_child, &to->d_child);
> - else
> - list_del_init(&cursor->d_child);
> + hlist_add_behind(&cursor->d_sib, &to->d_sib);
> spin_unlock(&dentry->d_lock);
> dput(to);
>
> @@ -187,19 +189,16 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
> {
> struct dentry *dentry = file->f_path.dentry;
> struct dentry *cursor = file->private_data;
> - struct list_head *anchor = &dentry->d_subdirs;
> struct dentry *next = NULL;
> - struct list_head *p;
> + struct hlist_node **p;
>
> if (!dir_emit_dots(file, ctx))
> return 0;
>
> if (ctx->pos == 2)
> - p = anchor;
> - else if (!list_empty(&cursor->d_child))
> - p = &cursor->d_child;
> + p = &dentry->d_children.first;
> else
> - return 0;
> + p = &cursor->d_sib.next;
>
> while ((next = scan_positives(cursor, p, 1, next)) != NULL) {
> if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
> @@ -207,13 +206,12 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
> fs_umode_to_dtype(d_inode(next)->i_mode)))
> break;
> ctx->pos++;
> - p = &next->d_child;
> + p = &next->d_sib.next;
> }
> spin_lock(&dentry->d_lock);
> + hlist_del_init(&cursor->d_sib);
> if (next)
> - list_move_tail(&cursor->d_child, &next->d_child);
> - else
> - list_del_init(&cursor->d_child);
> + hlist_add_before(&cursor->d_sib, &next->d_sib);
> spin_unlock(&dentry->d_lock);
> dput(next);
>
> @@ -492,12 +490,11 @@ const struct file_operations simple_offset_dir_operations = {
>
> static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
> {
> - struct dentry *child = NULL;
> - struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs;
> + struct dentry *child = NULL, *d;
>
> spin_lock(&parent->d_lock);
> - while ((p = p->next) != &parent->d_subdirs) {
> - struct dentry *d = container_of(p, struct dentry, d_child);
> + d = prev ? d_next_sibling(prev) : d_first_child(parent);
> + hlist_for_each_entry_from(d, d_sib) {
> if (simple_positive(d)) {
> spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
> if (simple_positive(d))
> @@ -658,7 +655,7 @@ int simple_empty(struct dentry *dentry)
> int ret = 0;
>
> spin_lock(&dentry->d_lock);
> - list_for_each_entry(child, &dentry->d_subdirs, d_child) {
> + hlist_for_each_entry(child, &dentry->d_children, d_sib) {
> spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
> if (simple_positive(child)) {
> spin_unlock(&child->d_lock);
> diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
> index 7974e91ffe13..8bfd690e9f10 100644
> --- a/fs/notify/fsnotify.c
> +++ b/fs/notify/fsnotify.c
> @@ -124,7 +124,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
> * d_flags to indicate parental interest (their parent is the
> * original inode) */
> spin_lock(&alias->d_lock);
> - list_for_each_entry(child, &alias->d_subdirs, d_child) {
> + hlist_for_each_entry(child, &alias->d_children, d_sib) {
> if (!child->d_inode)
> continue;
>
> diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
> index 5b54948514fe..61ca5fcf10f9 100644
> --- a/fs/tracefs/inode.c
> +++ b/fs/tracefs/inode.c
> @@ -199,26 +199,21 @@ static void change_gid(struct dentry *dentry, kgid_t gid)
> */
> static void set_gid(struct dentry *parent, kgid_t gid)
> {
> - struct dentry *this_parent;
> - struct list_head *next;
> + struct dentry *this_parent, *dentry;
>
> this_parent = parent;
> spin_lock(&this_parent->d_lock);
>
> change_gid(this_parent, gid);
> repeat:
> - next = this_parent->d_subdirs.next;
> + dentry = d_first_child(this_parent);
> resume:
> - while (next != &this_parent->d_subdirs) {
> - struct list_head *tmp = next;
> - struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
> - next = tmp->next;
> -
> + hlist_for_each_entry_from(dentry, d_sib) {
> spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
>
> change_gid(dentry, gid);
>
> - if (!list_empty(&dentry->d_subdirs)) {
> + if (!hlist_empty(&dentry->d_children)) {
> spin_unlock(&this_parent->d_lock);
> spin_release(&dentry->d_lock.dep_map, _RET_IP_);
> this_parent = dentry;
> @@ -233,21 +228,20 @@ static void set_gid(struct dentry *parent, kgid_t gid)
> rcu_read_lock();
> ascend:
> if (this_parent != parent) {
> - struct dentry *child = this_parent;
> - this_parent = child->d_parent;
> + dentry = this_parent;
> + this_parent = dentry->d_parent;
>
> - spin_unlock(&child->d_lock);
> + spin_unlock(&dentry->d_lock);
> spin_lock(&this_parent->d_lock);
>
> /* go into the first sibling still alive */
> - do {
> - next = child->d_child.next;
> - if (next == &this_parent->d_subdirs)
> - goto ascend;
> - child = list_entry(next, struct dentry, d_child);
> - } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
> - rcu_read_unlock();
> - goto resume;
> + hlist_for_each_entry_continue(dentry, d_sib) {
> + if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
> + rcu_read_unlock();
> + goto resume;
> + }
> + }
> + goto ascend;
> }
> rcu_read_unlock();
> spin_unlock(&this_parent->d_lock);
> diff --git a/include/linux/dcache.h b/include/linux/dcache.h
> index 3da2f0545d5d..0e397a0c519c 100644
> --- a/include/linux/dcache.h
> +++ b/include/linux/dcache.h
> @@ -68,12 +68,12 @@ extern const struct qstr dotdot_name;
> * large memory footprint increase).
> */
> #ifdef CONFIG_64BIT
> -# define DNAME_INLINE_LEN 32 /* 192 bytes */
> +# define DNAME_INLINE_LEN 40 /* 192 bytes */
> #else
> # ifdef CONFIG_SMP
> -# define DNAME_INLINE_LEN 36 /* 128 bytes */
> -# else
> # define DNAME_INLINE_LEN 40 /* 128 bytes */
> +# else
> +# define DNAME_INLINE_LEN 44 /* 128 bytes */
> # endif
> #endif
>
> @@ -101,8 +101,8 @@ struct dentry {
> struct list_head d_lru; /* LRU list */
> wait_queue_head_t *d_wait; /* in-lookup ones only */
> };
> - struct list_head d_child; /* child of parent list */
> - struct list_head d_subdirs; /* our children */
> + struct hlist_node d_sib; /* child of parent list */
> + struct hlist_head d_children; /* our children */
> /*
> * d_alias and d_rcu can share memory
> */
> @@ -600,4 +600,14 @@ struct name_snapshot {
> void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
> void release_dentry_name_snapshot(struct name_snapshot *);
>
> +static inline struct dentry *d_first_child(const struct dentry *dentry)
> +{
> + return hlist_entry_safe(dentry->d_children.first, struct dentry, d_sib);
> +}
> +
> +static inline struct dentry *d_next_sibling(const struct dentry *dentry)
> +{
> + return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib);
> +}
> +
> #endif /* __LINUX_DCACHE_H */
> --
> 2.39.2
>
>