@@ -170,15 +170,18 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
170
170
return - ENOENT ;
171
171
}
172
172
/*
173
- * At this point we either have a lease already and we can just
174
- * return it. If not we are guaranteed to be the only thread accessing
175
- * this cfid .
173
+ * Return cached fid if it has a lease. Otherwise, it is either a new
174
+ * entry or laundromat worker removed it from @cfids->entries. Caller
175
+ * will put last reference if the latter .
176
176
*/
177
+ spin_lock (& cfids -> cfid_list_lock );
177
178
if (cfid -> has_lease ) {
179
+ spin_unlock (& cfids -> cfid_list_lock );
178
180
* ret_cfid = cfid ;
179
181
kfree (utf16_path );
180
182
return 0 ;
181
183
}
184
+ spin_unlock (& cfids -> cfid_list_lock );
182
185
183
186
/*
184
187
* Skip any prefix paths in @path as lookup_positive_unlocked() ends up
@@ -295,9 +298,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
295
298
goto oshr_free ;
296
299
}
297
300
}
301
+ spin_lock (& cfids -> cfid_list_lock );
298
302
cfid -> dentry = dentry ;
299
303
cfid -> time = jiffies ;
300
304
cfid -> has_lease = true;
305
+ spin_unlock (& cfids -> cfid_list_lock );
301
306
302
307
oshr_free :
303
308
kfree (utf16_path );
@@ -306,32 +311,36 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
306
311
free_rsp_buf (resp_buftype [0 ], rsp_iov [0 ].iov_base );
307
312
free_rsp_buf (resp_buftype [1 ], rsp_iov [1 ].iov_base );
308
313
spin_lock (& cfids -> cfid_list_lock );
309
- if (rc && !cfid -> has_lease ) {
310
- if (cfid -> on_list ) {
311
- list_del (& cfid -> entry );
312
- cfid -> on_list = false;
313
- cfids -> num_entries -- ;
314
+ if (!cfid -> has_lease ) {
315
+ if (rc ) {
316
+ if (cfid -> on_list ) {
317
+ list_del (& cfid -> entry );
318
+ cfid -> on_list = false;
319
+ cfids -> num_entries -- ;
320
+ }
321
+ rc = - ENOENT ;
322
+ } else {
323
+ /*
324
+ * We are guaranteed to have two references at this
325
+ * point. One for the caller and one for a potential
326
+ * lease. Release the Lease-ref so that the directory
327
+ * will be closed when the caller closes the cached
328
+ * handle.
329
+ */
330
+ spin_unlock (& cfids -> cfid_list_lock );
331
+ kref_put (& cfid -> refcount , smb2_close_cached_fid );
332
+ goto out ;
314
333
}
315
- rc = - ENOENT ;
316
334
}
317
335
spin_unlock (& cfids -> cfid_list_lock );
318
- if (!rc && !cfid -> has_lease ) {
319
- /*
320
- * We are guaranteed to have two references at this point.
321
- * One for the caller and one for a potential lease.
322
- * Release the Lease-ref so that the directory will be closed
323
- * when the caller closes the cached handle.
324
- */
325
- kref_put (& cfid -> refcount , smb2_close_cached_fid );
326
- }
327
336
if (rc ) {
328
337
if (cfid -> is_open )
329
338
SMB2_close (0 , cfid -> tcon , cfid -> fid .persistent_fid ,
330
339
cfid -> fid .volatile_fid );
331
340
free_cached_dir (cfid );
332
341
cfid = NULL ;
333
342
}
334
-
343
+ out :
335
344
if (rc == 0 ) {
336
345
* ret_cfid = cfid ;
337
346
atomic_inc (& tcon -> num_remote_opens );
@@ -583,15 +592,18 @@ static void cfids_laundromat_worker(struct work_struct *work)
583
592
584
593
spin_lock (& cfids -> cfid_list_lock );
585
594
list_for_each_entry_safe (cfid , q , & cfids -> entries , entry ) {
586
- if (time_after (jiffies , cfid -> time + HZ * dir_cache_timeout )) {
595
+ if (cfid -> time &&
596
+ time_after (jiffies , cfid -> time + HZ * dir_cache_timeout )) {
597
+ cfid -> on_list = false;
587
598
list_move (& cfid -> entry , & entry );
588
599
cfids -> num_entries -- ;
600
+ /* To prevent race with smb2_cached_lease_break() */
601
+ kref_get (& cfid -> refcount );
589
602
}
590
603
}
591
604
spin_unlock (& cfids -> cfid_list_lock );
592
605
593
606
list_for_each_entry_safe (cfid , q , & entry , entry ) {
594
- cfid -> on_list = false;
595
607
list_del (& cfid -> entry );
596
608
/*
597
609
* Cancel and wait for the work to finish in case we are racing
@@ -608,6 +620,8 @@ static void cfids_laundromat_worker(struct work_struct *work)
608
620
spin_unlock (& cfids -> cfid_list_lock );
609
621
kref_put (& cfid -> refcount , smb2_close_cached_fid );
610
622
}
623
+ /* Drop the extra reference opened above */
624
+ kref_put (& cfid -> refcount , smb2_close_cached_fid );
611
625
}
612
626
queue_delayed_work (cifsiod_wq , & cfids -> laundromat_work ,
613
627
dir_cache_timeout * HZ );
0 commit comments