summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2019-07-24 23:01:55 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2019-07-24 23:02:21 -0400
commitff09297ec9964b3fe4bade77c92c75fde34fa8e9 (patch)
tree145157267c90462a14ecaec483657d796a37e5d5
parent5f9e832c137075045d15cd6899ab0505cfb2ca4b (diff)
autofs: simplify get_next_positive_...(), get rid of trylocks
* new helper: positive_after(parent, child); parent->d_lock is held by caller, grabs and returns the first thing after child in the list of children that has simple_positive() true. NULL if nothing's found; NULL child == search the entire list. * get_next_positive_subdir() loses the redundant check for d_count and switches to use of that helper. BTW, dput(NULL) is a no-op for a good reason... * get_next_positive_dentry() switched to the same helper. Logics: look for positive child in prev; if not found, look for the positive child of prev's parent following prev, etc. That way we are guaranteed that we are only moving rootwards through the ancestors of prev, which is pinned and thus not going anywhere. Since ->d_parent on autofs never changes, the same goes for the entire chain of ancestors and we don't need overlapping ->d_lock on them. Which avoids the trylock loops, in addition to simplifying the logics in there... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/autofs/expire.c103
1 files changed, 32 insertions, 71 deletions
diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
index cdff0567aacb..2866fabf497f 100644
--- a/fs/autofs/expire.c
+++ b/fs/autofs/expire.c
@@ -70,6 +70,27 @@ done:
70 return status; 70 return status;
71} 71}
72 72
73/* p->d_lock held */
74static 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
73/* 94/*
74 * Calculate and dget next entry in the subdirs list under root. 95 * Calculate and dget next entry in the subdirs list under root.
75 */ 96 */
@@ -77,43 +98,14 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
77 struct dentry *root) 98 struct dentry *root)
78{ 99{
79 struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); 100 struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
80 struct list_head *next;
81 struct dentry *q; 101 struct dentry *q;
82 102
83 spin_lock(&sbi->lookup_lock); 103 spin_lock(&sbi->lookup_lock);
84 spin_lock(&root->d_lock); 104 spin_lock(&root->d_lock);
85 105 q = positive_after(root, prev);
86 if (prev)
87 next = prev->d_child.next;
88 else {
89 prev = dget_dlock(root);
90 next = prev->d_subdirs.next;
91 }
92
93cont:
94 if (next == &root->d_subdirs) {
95 spin_unlock(&root->d_lock);
96 spin_unlock(&sbi->lookup_lock);
97 dput(prev);
98 return NULL;
99 }
100
101 q = list_entry(next, struct dentry, d_child);
102
103 spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
104 /* Already gone or negative dentry (under construction) - try next */
105 if (!d_count(q) || !simple_positive(q)) {
106 spin_unlock(&q->d_lock);
107 next = q->d_child.next;
108 goto cont;
109 }
110 dget_dlock(q);
111 spin_unlock(&q->d_lock);
112 spin_unlock(&root->d_lock); 106 spin_unlock(&root->d_lock);
113 spin_unlock(&sbi->lookup_lock); 107 spin_unlock(&sbi->lookup_lock);
114
115 dput(prev); 108 dput(prev);
116
117 return q; 109 return q;
118} 110}
119 111
@@ -124,59 +116,28 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
124 struct dentry *root) 116 struct dentry *root)
125{ 117{
126 struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); 118 struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
127 struct list_head *next; 119 struct dentry *p = prev, *ret = NULL, *d = NULL;
128 struct dentry *p, *ret;
129 120
130 if (prev == NULL) 121 if (prev == NULL)
131 return dget(root); 122 return dget(root);
132 123
133 spin_lock(&sbi->lookup_lock); 124 spin_lock(&sbi->lookup_lock);
134relock:
135 p = prev;
136 spin_lock(&p->d_lock); 125 spin_lock(&p->d_lock);
137again: 126 while (1) {
138 next = p->d_subdirs.next; 127 struct dentry *parent;
139 if (next == &p->d_subdirs) {
140 while (1) {
141 struct dentry *parent;
142
143 if (p == root) {
144 spin_unlock(&p->d_lock);
145 spin_unlock(&sbi->lookup_lock);
146 dput(prev);
147 return NULL;
148 }
149 128
150 parent = p->d_parent; 129 ret = positive_after(p, d);
151 if (!spin_trylock(&parent->d_lock)) { 130 if (ret || p == root)
152 spin_unlock(&p->d_lock); 131 break;
153 cpu_relax(); 132 parent = p->d_parent;
154 goto relock;
155 }
156 spin_unlock(&p->d_lock);
157 next = p->d_child.next;
158 p = parent;
159 if (next != &parent->d_subdirs)
160 break;
161 }
162 }
163 ret = list_entry(next, struct dentry, d_child);
164
165 spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
166 /* Negative dentry - try next */
167 if (!simple_positive(ret)) {
168 spin_unlock(&p->d_lock); 133 spin_unlock(&p->d_lock);
169 lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_); 134 spin_lock(&parent->d_lock);
170 p = ret; 135 d = p;
171 goto again; 136 p = parent;
172 } 137 }
173 dget_dlock(ret);
174 spin_unlock(&ret->d_lock);
175 spin_unlock(&p->d_lock); 138 spin_unlock(&p->d_lock);
176 spin_unlock(&sbi->lookup_lock); 139 spin_unlock(&sbi->lookup_lock);
177
178 dput(prev); 140 dput(prev);
179
180 return ret; 141 return ret;
181} 142}
182 143