.. | .. |
---|
| 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 | | -#include <linux/delay.h> |
---|
12 | 8 | #include "autofs_i.h" |
---|
13 | 9 | |
---|
14 | 10 | /* Check if a dentry can be expired */ |
---|
.. | .. |
---|
74 | 70 | return status; |
---|
75 | 71 | } |
---|
76 | 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 | + |
---|
77 | 94 | /* |
---|
78 | 95 | * Calculate and dget next entry in the subdirs list under root. |
---|
79 | 96 | */ |
---|
.. | .. |
---|
81 | 98 | struct dentry *root) |
---|
82 | 99 | { |
---|
83 | 100 | struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); |
---|
84 | | - struct list_head *next; |
---|
85 | 101 | struct dentry *q; |
---|
86 | 102 | |
---|
87 | 103 | spin_lock(&sbi->lookup_lock); |
---|
88 | 104 | spin_lock(&root->d_lock); |
---|
89 | | - |
---|
90 | | - if (prev) |
---|
91 | | - next = prev->d_child.next; |
---|
92 | | - else { |
---|
93 | | - prev = dget_dlock(root); |
---|
94 | | - next = prev->d_subdirs.next; |
---|
95 | | - } |
---|
96 | | - |
---|
97 | | -cont: |
---|
98 | | - if (next == &root->d_subdirs) { |
---|
99 | | - spin_unlock(&root->d_lock); |
---|
100 | | - spin_unlock(&sbi->lookup_lock); |
---|
101 | | - dput(prev); |
---|
102 | | - return NULL; |
---|
103 | | - } |
---|
104 | | - |
---|
105 | | - q = list_entry(next, struct dentry, d_child); |
---|
106 | | - |
---|
107 | | - spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); |
---|
108 | | - /* Already gone or negative dentry (under construction) - try next */ |
---|
109 | | - if (!d_count(q) || !simple_positive(q)) { |
---|
110 | | - spin_unlock(&q->d_lock); |
---|
111 | | - next = q->d_child.next; |
---|
112 | | - goto cont; |
---|
113 | | - } |
---|
114 | | - dget_dlock(q); |
---|
115 | | - spin_unlock(&q->d_lock); |
---|
| 105 | + q = positive_after(root, prev); |
---|
116 | 106 | spin_unlock(&root->d_lock); |
---|
117 | 107 | spin_unlock(&sbi->lookup_lock); |
---|
118 | | - |
---|
119 | 108 | dput(prev); |
---|
120 | | - |
---|
121 | 109 | return q; |
---|
122 | 110 | } |
---|
123 | 111 | |
---|
.. | .. |
---|
128 | 116 | struct dentry *root) |
---|
129 | 117 | { |
---|
130 | 118 | struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); |
---|
131 | | - struct list_head *next; |
---|
132 | | - struct dentry *p, *ret; |
---|
| 119 | + struct dentry *p = prev, *ret = NULL, *d = NULL; |
---|
133 | 120 | |
---|
134 | 121 | if (prev == NULL) |
---|
135 | 122 | return dget(root); |
---|
136 | 123 | |
---|
137 | 124 | spin_lock(&sbi->lookup_lock); |
---|
138 | | -relock: |
---|
139 | | - p = prev; |
---|
140 | 125 | spin_lock(&p->d_lock); |
---|
141 | | -again: |
---|
142 | | - next = p->d_subdirs.next; |
---|
143 | | - if (next == &p->d_subdirs) { |
---|
144 | | - while (1) { |
---|
145 | | - struct dentry *parent; |
---|
| 126 | + while (1) { |
---|
| 127 | + struct dentry *parent; |
---|
146 | 128 | |
---|
147 | | - if (p == root) { |
---|
148 | | - spin_unlock(&p->d_lock); |
---|
149 | | - spin_unlock(&sbi->lookup_lock); |
---|
150 | | - dput(prev); |
---|
151 | | - return NULL; |
---|
152 | | - } |
---|
153 | | - |
---|
154 | | - parent = p->d_parent; |
---|
155 | | - if (!spin_trylock(&parent->d_lock)) { |
---|
156 | | - spin_unlock(&p->d_lock); |
---|
157 | | - cpu_chill(); |
---|
158 | | - goto relock; |
---|
159 | | - } |
---|
160 | | - spin_unlock(&p->d_lock); |
---|
161 | | - next = p->d_child.next; |
---|
162 | | - p = parent; |
---|
163 | | - if (next != &parent->d_subdirs) |
---|
164 | | - break; |
---|
165 | | - } |
---|
166 | | - } |
---|
167 | | - ret = list_entry(next, struct dentry, d_child); |
---|
168 | | - |
---|
169 | | - spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); |
---|
170 | | - /* Negative dentry - try next */ |
---|
171 | | - if (!simple_positive(ret)) { |
---|
| 129 | + ret = positive_after(p, d); |
---|
| 130 | + if (ret || p == root) |
---|
| 131 | + break; |
---|
| 132 | + parent = p->d_parent; |
---|
172 | 133 | spin_unlock(&p->d_lock); |
---|
173 | | - lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_); |
---|
174 | | - p = ret; |
---|
175 | | - goto again; |
---|
| 134 | + spin_lock(&parent->d_lock); |
---|
| 135 | + d = p; |
---|
| 136 | + p = parent; |
---|
176 | 137 | } |
---|
177 | | - dget_dlock(ret); |
---|
178 | | - spin_unlock(&ret->d_lock); |
---|
179 | 138 | spin_unlock(&p->d_lock); |
---|
180 | 139 | spin_unlock(&sbi->lookup_lock); |
---|
181 | | - |
---|
182 | 140 | dput(prev); |
---|
183 | | - |
---|
184 | 141 | return ret; |
---|
185 | 142 | } |
---|
186 | 143 | |
---|
.. | .. |
---|
254 | 211 | } |
---|
255 | 212 | } else { |
---|
256 | 213 | struct autofs_info *ino = autofs_dentry_ino(p); |
---|
257 | | - unsigned int ino_count = atomic_read(&ino->count); |
---|
| 214 | + unsigned int ino_count = READ_ONCE(ino->count); |
---|
258 | 215 | |
---|
259 | 216 | /* allow for dget above and top is already dgot */ |
---|
260 | 217 | if (p == top) |
---|
.. | .. |
---|
422 | 379 | /* Not a forced expire? */ |
---|
423 | 380 | if (!(how & AUTOFS_EXP_FORCED)) { |
---|
424 | 381 | /* ref-walk currently on this dentry? */ |
---|
425 | | - ino_count = atomic_read(&ino->count) + 1; |
---|
| 382 | + ino_count = READ_ONCE(ino->count) + 1; |
---|
426 | 383 | if (d_count(dentry) > ino_count) |
---|
427 | 384 | return NULL; |
---|
428 | 385 | } |
---|
.. | .. |
---|
439 | 396 | /* Not a forced expire? */ |
---|
440 | 397 | if (!(how & AUTOFS_EXP_FORCED)) { |
---|
441 | 398 | /* ref-walk currently on this dentry? */ |
---|
442 | | - ino_count = atomic_read(&ino->count) + 1; |
---|
| 399 | + ino_count = READ_ONCE(ino->count) + 1; |
---|
443 | 400 | if (d_count(dentry) > ino_count) |
---|
444 | 401 | return NULL; |
---|
445 | 402 | } |
---|