@@ -89,58 +89,45 @@ int dcache_dir_close(struct inode *inode, struct file *file)
89
89
EXPORT_SYMBOL (dcache_dir_close );
90
90
91
91
/* parent is locked at least shared */
92
- static struct dentry * next_positive (struct dentry * parent ,
93
- struct list_head * from ,
94
- int count )
92
+ /*
93
+ * Returns an element of siblings' list.
94
+ * We are looking for <count>th positive after <p>; if
95
+ * found, dentry is grabbed and returned to caller.
96
+ * If no such element exists, NULL is returned.
97
+ */
98
+ static struct dentry * scan_positives (struct dentry * cursor ,
99
+ struct list_head * p ,
100
+ loff_t count ,
101
+ struct dentry * last )
95
102
{
96
- unsigned * seq = & parent -> d_inode -> i_dir_seq , n ;
97
- struct dentry * res ;
98
- struct list_head * p ;
99
- bool skipped ;
100
- int i ;
103
+ struct dentry * dentry = cursor -> d_parent , * found = NULL ;
101
104
102
- retry :
103
- i = count ;
104
- skipped = false;
105
- n = smp_load_acquire (seq ) & ~1 ;
106
- res = NULL ;
107
- rcu_read_lock ();
108
- for (p = from -> next ; p != & parent -> d_subdirs ; p = p -> next ) {
105
+ spin_lock (& dentry -> d_lock );
106
+ while ((p = p -> next ) != & dentry -> d_subdirs ) {
109
107
struct dentry * d = list_entry (p , struct dentry , d_child );
110
- if (!simple_positive (d )) {
111
- skipped = true;
112
- } else if (!-- i ) {
113
- res = d ;
114
- break ;
108
+ // we must at least skip cursors, to avoid livelocks
109
+ if (d -> d_flags & DCACHE_DENTRY_CURSOR )
110
+ continue ;
111
+ if (simple_positive (d ) && !-- count ) {
112
+ spin_lock_nested (& d -> d_lock , DENTRY_D_LOCK_NESTED );
113
+ if (simple_positive (d ))
114
+ found = dget_dlock (d );
115
+ spin_unlock (& d -> d_lock );
116
+ if (likely (found ))
117
+ break ;
118
+ count = 1 ;
119
+ }
120
+ if (need_resched ()) {
121
+ list_move (& cursor -> d_child , p );
122
+ p = & cursor -> d_child ;
123
+ spin_unlock (& dentry -> d_lock );
124
+ cond_resched ();
125
+ spin_lock (& dentry -> d_lock );
115
126
}
116
127
}
117
- rcu_read_unlock ();
118
- if (skipped ) {
119
- smp_rmb ();
120
- if (unlikely (* seq != n ))
121
- goto retry ;
122
- }
123
- return res ;
124
- }
125
-
126
- static void move_cursor (struct dentry * cursor , struct list_head * after )
127
- {
128
- struct dentry * parent = cursor -> d_parent ;
129
- unsigned n , * seq = & parent -> d_inode -> i_dir_seq ;
130
- spin_lock (& parent -> d_lock );
131
- for (;;) {
132
- n = * seq ;
133
- if (!(n & 1 ) && cmpxchg (seq , n , n + 1 ) == n )
134
- break ;
135
- cpu_relax ();
136
- }
137
- __list_del (cursor -> d_child .prev , cursor -> d_child .next );
138
- if (after )
139
- list_add (& cursor -> d_child , after );
140
- else
141
- list_add_tail (& cursor -> d_child , & parent -> d_subdirs );
142
- smp_store_release (seq , n + 2 );
143
- spin_unlock (& parent -> d_lock );
128
+ spin_unlock (& dentry -> d_lock );
129
+ dput (last );
130
+ return found ;
144
131
}
145
132
146
133
loff_t dcache_dir_lseek (struct file * file , loff_t offset , int whence )
@@ -158,17 +145,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
158
145
return - EINVAL ;
159
146
}
160
147
if (offset != file -> f_pos ) {
148
+ struct dentry * cursor = file -> private_data ;
149
+ struct dentry * to = NULL ;
150
+
151
+ inode_lock_shared (dentry -> d_inode );
152
+
153
+ if (offset > 2 )
154
+ to = scan_positives (cursor , & dentry -> d_subdirs ,
155
+ offset - 2 , NULL );
156
+ spin_lock (& dentry -> d_lock );
157
+ if (to )
158
+ list_move (& cursor -> d_child , & to -> d_child );
159
+ else
160
+ list_del_init (& cursor -> d_child );
161
+ spin_unlock (& dentry -> d_lock );
162
+ dput (to );
163
+
161
164
file -> f_pos = offset ;
162
- if (file -> f_pos >= 2 ) {
163
- struct dentry * cursor = file -> private_data ;
164
- struct dentry * to ;
165
- loff_t n = file -> f_pos - 2 ;
166
-
167
- inode_lock_shared (dentry -> d_inode );
168
- to = next_positive (dentry , & dentry -> d_subdirs , n );
169
- move_cursor (cursor , to ? & to -> d_child : NULL );
170
- inode_unlock_shared (dentry -> d_inode );
171
- }
165
+
166
+ inode_unlock_shared (dentry -> d_inode );
172
167
}
173
168
return offset ;
174
169
}
@@ -190,25 +185,35 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
190
185
{
191
186
struct dentry * dentry = file -> f_path .dentry ;
192
187
struct dentry * cursor = file -> private_data ;
193
- struct list_head * p = & cursor -> d_child ;
194
- struct dentry * next ;
195
- bool moved = false ;
188
+ struct list_head * anchor = & dentry -> d_subdirs ;
189
+ struct dentry * next = NULL ;
190
+ struct list_head * p ;
196
191
197
192
if (!dir_emit_dots (file , ctx ))
198
193
return 0 ;
199
194
200
195
if (ctx -> pos == 2 )
201
- p = & dentry -> d_subdirs ;
202
- while ((next = next_positive (dentry , p , 1 )) != NULL ) {
196
+ p = anchor ;
197
+ else if (!list_empty (& cursor -> d_child ))
198
+ p = & cursor -> d_child ;
199
+ else
200
+ return 0 ;
201
+
202
+ while ((next = scan_positives (cursor , p , 1 , next )) != NULL ) {
203
203
if (!dir_emit (ctx , next -> d_name .name , next -> d_name .len ,
204
204
d_inode (next )-> i_ino , dt_type (d_inode (next ))))
205
205
break ;
206
- moved = true;
207
- p = & next -> d_child ;
208
206
ctx -> pos ++ ;
207
+ p = & next -> d_child ;
209
208
}
210
- if (moved )
211
- move_cursor (cursor , p );
209
+ spin_lock (& dentry -> d_lock );
210
+ if (next )
211
+ list_move_tail (& cursor -> d_child , & next -> d_child );
212
+ else
213
+ list_del_init (& cursor -> d_child );
214
+ spin_unlock (& dentry -> d_lock );
215
+ dput (next );
216
+
212
217
return 0 ;
213
218
}
214
219
EXPORT_SYMBOL (dcache_readdir );
0 commit comments