| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved |
|---|
| 3 | 4 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> |
|---|
| 4 | 5 | * Copyright 2001-2006 Ian Kent <raven@themaw.net> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This file is part of the Linux kernel and is made available under |
|---|
| 7 | | - * the terms of the GNU General Public License, version 2, or at your |
|---|
| 8 | | - * option, any later version, incorporated herein by reference. |
|---|
| 9 | 6 | */ |
|---|
| 10 | 7 | |
|---|
| 11 | 8 | #include "autofs_i.h" |
|---|
| .. | .. |
|---|
| 73 | 70 | return status; |
|---|
| 74 | 71 | } |
|---|
| 75 | 72 | |
|---|
| 73 | +/* p->d_lock held */ |
|---|
| 74 | +static struct dentry *positive_after(struct dentry *p, struct dentry *child) |
|---|
| 75 | +{ |
|---|
| 76 | + if (child) |
|---|
| 77 | + child = list_next_entry(child, d_child); |
|---|
| 78 | + else |
|---|
| 79 | + child = list_first_entry(&p->d_subdirs, struct dentry, d_child); |
|---|
| 80 | + |
|---|
| 81 | + list_for_each_entry_from(child, &p->d_subdirs, d_child) { |
|---|
| 82 | + spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); |
|---|
| 83 | + if (simple_positive(child)) { |
|---|
| 84 | + dget_dlock(child); |
|---|
| 85 | + spin_unlock(&child->d_lock); |
|---|
| 86 | + return child; |
|---|
| 87 | + } |
|---|
| 88 | + spin_unlock(&child->d_lock); |
|---|
| 89 | + } |
|---|
| 90 | + |
|---|
| 91 | + return NULL; |
|---|
| 92 | +} |
|---|
| 93 | + |
|---|
| 76 | 94 | /* |
|---|
| 77 | 95 | * Calculate and dget next entry in the subdirs list under root. |
|---|
| 78 | 96 | */ |
|---|
| .. | .. |
|---|
| 80 | 98 | struct dentry *root) |
|---|
| 81 | 99 | { |
|---|
| 82 | 100 | struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); |
|---|
| 83 | | - struct list_head *next; |
|---|
| 84 | 101 | struct dentry *q; |
|---|
| 85 | 102 | |
|---|
| 86 | 103 | spin_lock(&sbi->lookup_lock); |
|---|
| 87 | 104 | spin_lock(&root->d_lock); |
|---|
| 88 | | - |
|---|
| 89 | | - if (prev) |
|---|
| 90 | | - next = prev->d_child.next; |
|---|
| 91 | | - else { |
|---|
| 92 | | - prev = dget_dlock(root); |
|---|
| 93 | | - next = prev->d_subdirs.next; |
|---|
| 94 | | - } |
|---|
| 95 | | - |
|---|
| 96 | | -cont: |
|---|
| 97 | | - if (next == &root->d_subdirs) { |
|---|
| 98 | | - spin_unlock(&root->d_lock); |
|---|
| 99 | | - spin_unlock(&sbi->lookup_lock); |
|---|
| 100 | | - dput(prev); |
|---|
| 101 | | - return NULL; |
|---|
| 102 | | - } |
|---|
| 103 | | - |
|---|
| 104 | | - q = list_entry(next, struct dentry, d_child); |
|---|
| 105 | | - |
|---|
| 106 | | - spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); |
|---|
| 107 | | - /* Already gone or negative dentry (under construction) - try next */ |
|---|
| 108 | | - if (!d_count(q) || !simple_positive(q)) { |
|---|
| 109 | | - spin_unlock(&q->d_lock); |
|---|
| 110 | | - next = q->d_child.next; |
|---|
| 111 | | - goto cont; |
|---|
| 112 | | - } |
|---|
| 113 | | - dget_dlock(q); |
|---|
| 114 | | - spin_unlock(&q->d_lock); |
|---|
| 105 | + q = positive_after(root, prev); |
|---|
| 115 | 106 | spin_unlock(&root->d_lock); |
|---|
| 116 | 107 | spin_unlock(&sbi->lookup_lock); |
|---|
| 117 | | - |
|---|
| 118 | 108 | dput(prev); |
|---|
| 119 | | - |
|---|
| 120 | 109 | return q; |
|---|
| 121 | 110 | } |
|---|
| 122 | 111 | |
|---|
| .. | .. |
|---|
| 127 | 116 | struct dentry *root) |
|---|
| 128 | 117 | { |
|---|
| 129 | 118 | struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); |
|---|
| 130 | | - struct list_head *next; |
|---|
| 131 | | - struct dentry *p, *ret; |
|---|
| 119 | + struct dentry *p = prev, *ret = NULL, *d = NULL; |
|---|
| 132 | 120 | |
|---|
| 133 | 121 | if (prev == NULL) |
|---|
| 134 | 122 | return dget(root); |
|---|
| 135 | 123 | |
|---|
| 136 | 124 | spin_lock(&sbi->lookup_lock); |
|---|
| 137 | | -relock: |
|---|
| 138 | | - p = prev; |
|---|
| 139 | 125 | spin_lock(&p->d_lock); |
|---|
| 140 | | -again: |
|---|
| 141 | | - next = p->d_subdirs.next; |
|---|
| 142 | | - if (next == &p->d_subdirs) { |
|---|
| 143 | | - while (1) { |
|---|
| 144 | | - struct dentry *parent; |
|---|
| 126 | + while (1) { |
|---|
| 127 | + struct dentry *parent; |
|---|
| 145 | 128 | |
|---|
| 146 | | - if (p == root) { |
|---|
| 147 | | - spin_unlock(&p->d_lock); |
|---|
| 148 | | - spin_unlock(&sbi->lookup_lock); |
|---|
| 149 | | - dput(prev); |
|---|
| 150 | | - return NULL; |
|---|
| 151 | | - } |
|---|
| 152 | | - |
|---|
| 153 | | - parent = p->d_parent; |
|---|
| 154 | | - if (!spin_trylock(&parent->d_lock)) { |
|---|
| 155 | | - spin_unlock(&p->d_lock); |
|---|
| 156 | | - cpu_relax(); |
|---|
| 157 | | - goto relock; |
|---|
| 158 | | - } |
|---|
| 159 | | - spin_unlock(&p->d_lock); |
|---|
| 160 | | - next = p->d_child.next; |
|---|
| 161 | | - p = parent; |
|---|
| 162 | | - if (next != &parent->d_subdirs) |
|---|
| 163 | | - break; |
|---|
| 164 | | - } |
|---|
| 165 | | - } |
|---|
| 166 | | - ret = list_entry(next, struct dentry, d_child); |
|---|
| 167 | | - |
|---|
| 168 | | - spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); |
|---|
| 169 | | - /* Negative dentry - try next */ |
|---|
| 170 | | - if (!simple_positive(ret)) { |
|---|
| 129 | + ret = positive_after(p, d); |
|---|
| 130 | + if (ret || p == root) |
|---|
| 131 | + break; |
|---|
| 132 | + parent = p->d_parent; |
|---|
| 171 | 133 | spin_unlock(&p->d_lock); |
|---|
| 172 | | - lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_); |
|---|
| 173 | | - p = ret; |
|---|
| 174 | | - goto again; |
|---|
| 134 | + spin_lock(&parent->d_lock); |
|---|
| 135 | + d = p; |
|---|
| 136 | + p = parent; |
|---|
| 175 | 137 | } |
|---|
| 176 | | - dget_dlock(ret); |
|---|
| 177 | | - spin_unlock(&ret->d_lock); |
|---|
| 178 | 138 | spin_unlock(&p->d_lock); |
|---|
| 179 | 139 | spin_unlock(&sbi->lookup_lock); |
|---|
| 180 | | - |
|---|
| 181 | 140 | dput(prev); |
|---|
| 182 | | - |
|---|
| 183 | 141 | return ret; |
|---|
| 184 | 142 | } |
|---|
| 185 | 143 | |
|---|
| .. | .. |
|---|
| 253 | 211 | } |
|---|
| 254 | 212 | } else { |
|---|
| 255 | 213 | struct autofs_info *ino = autofs_dentry_ino(p); |
|---|
| 256 | | - unsigned int ino_count = atomic_read(&ino->count); |
|---|
| 214 | + unsigned int ino_count = READ_ONCE(ino->count); |
|---|
| 257 | 215 | |
|---|
| 258 | 216 | /* allow for dget above and top is already dgot */ |
|---|
| 259 | 217 | if (p == top) |
|---|
| .. | .. |
|---|
| 421 | 379 | /* Not a forced expire? */ |
|---|
| 422 | 380 | if (!(how & AUTOFS_EXP_FORCED)) { |
|---|
| 423 | 381 | /* ref-walk currently on this dentry? */ |
|---|
| 424 | | - ino_count = atomic_read(&ino->count) + 1; |
|---|
| 382 | + ino_count = READ_ONCE(ino->count) + 1; |
|---|
| 425 | 383 | if (d_count(dentry) > ino_count) |
|---|
| 426 | 384 | return NULL; |
|---|
| 427 | 385 | } |
|---|
| .. | .. |
|---|
| 438 | 396 | /* Not a forced expire? */ |
|---|
| 439 | 397 | if (!(how & AUTOFS_EXP_FORCED)) { |
|---|
| 440 | 398 | /* ref-walk currently on this dentry? */ |
|---|
| 441 | | - ino_count = atomic_read(&ino->count) + 1; |
|---|
| 399 | + ino_count = READ_ONCE(ino->count) + 1; |
|---|
| 442 | 400 | if (d_count(dentry) > ino_count) |
|---|
| 443 | 401 | return NULL; |
|---|
| 444 | 402 | } |
|---|