Skip to content

Commit 3afca26

Browse files
committed
Clarify locking of cifs file and tcon structures and make more granular
Remove the global file_list_lock to simplify cifs/smb3 locking and have spinlocks that more closely match the information they are protecting. Add new tcon->open_file_lock and file->file_info_lock spinlocks. Locks continue to follow a heirachy, cifs_socket --> cifs_ses --> cifs_tcon --> cifs_file where global tcp_ses_lock still protects socket and cifs_ses, while the the newer locks protect the lower level structure's information (tcon and cifs_file respectively). CC: Stable <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Pavel Shilovsky <[email protected]> Reviewed-by: Aurelien Aptel <[email protected]> Reviewed-by: Germano Percossi <[email protected]>
1 parent d171356 commit 3afca26

File tree

7 files changed

+75
-63
lines changed

7 files changed

+75
-63
lines changed

fs/cifs/cifsfs.c

-1
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,6 @@ init_cifs(void)
12621262
GlobalTotalActiveXid = 0;
12631263
GlobalMaxActiveXid = 0;
12641264
spin_lock_init(&cifs_tcp_ses_lock);
1265-
spin_lock_init(&cifs_file_list_lock);
12661265
spin_lock_init(&GlobalMid_Lock);
12671266

12681267
get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret));

fs/cifs/cifsglob.h

+15-15
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ struct cifs_tcon {
833833
struct list_head tcon_list;
834834
int tc_count;
835835
struct list_head openFileList;
836+
spinlock_t open_file_lock; /* protects list above */
836837
struct cifs_ses *ses; /* pointer to session associated with */
837838
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
838839
char *nativeFileSystem;
@@ -889,7 +890,7 @@ struct cifs_tcon {
889890
#endif /* CONFIG_CIFS_STATS2 */
890891
__u64 bytes_read;
891892
__u64 bytes_written;
892-
spinlock_t stat_lock;
893+
spinlock_t stat_lock; /* protects the two fields above */
893894
#endif /* CONFIG_CIFS_STATS */
894895
FILE_SYSTEM_DEVICE_INFO fsDevInfo;
895896
FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
@@ -1040,20 +1041,23 @@ struct cifs_fid_locks {
10401041
};
10411042

10421043
struct cifsFileInfo {
1044+
/* following two lists are protected by tcon->open_file_lock */
10431045
struct list_head tlist; /* pointer to next fid owned by tcon */
10441046
struct list_head flist; /* next fid (file instance) for this inode */
1047+
/* lock list below protected by cifsi->lock_sem */
10451048
struct cifs_fid_locks *llist; /* brlocks held by this fid */
10461049
kuid_t uid; /* allows finding which FileInfo structure */
10471050
__u32 pid; /* process id who opened file */
10481051
struct cifs_fid fid; /* file id from remote */
10491052
/* BB add lock scope info here if needed */ ;
10501053
/* lock scope id (0 if none) */
10511054
struct dentry *dentry;
1052-
unsigned int f_flags;
10531055
struct tcon_link *tlink;
1056+
unsigned int f_flags;
10541057
bool invalidHandle:1; /* file closed via session abend */
10551058
bool oplock_break_cancelled:1;
1056-
int count; /* refcount protected by cifs_file_list_lock */
1059+
int count;
1060+
spinlock_t file_info_lock; /* protects four flag/count fields above */
10571061
struct mutex fh_mutex; /* prevents reopen race after dead ses*/
10581062
struct cifs_search_info srch_inf;
10591063
struct work_struct oplock_break; /* work for oplock breaks */
@@ -1120,7 +1124,7 @@ struct cifs_writedata {
11201124

11211125
/*
11221126
* Take a reference on the file private data. Must be called with
1123-
* cifs_file_list_lock held.
1127+
* cfile->file_info_lock held.
11241128
*/
11251129
static inline void
11261130
cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
@@ -1514,8 +1518,10 @@ require use of the stronger protocol */
15141518
* GlobalMid_Lock protects:
15151519
* list operations on pending_mid_q and oplockQ
15161520
* updates to XID counters, multiplex id and SMB sequence numbers
1517-
* cifs_file_list_lock protects:
1518-
* list operations on tcp and SMB session lists and tCon lists
1521+
* tcp_ses_lock protects:
1522+
* list operations on tcp and SMB session lists
1523+
* tcon->open_file_lock protects the list of open files hanging off the tcon
1524+
* cfile->file_info_lock protects counters and fields in cifs file struct
15191525
* f_owner.lock protects certain per file struct operations
15201526
* mapping->page_lock protects certain per page operations
15211527
*
@@ -1547,18 +1553,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
15471553
* tcp session, and the list of tcon's per smb session. It also protects
15481554
* the reference counters for the server, smb session, and tcon. Finally,
15491555
* changes to the tcon->tidStatus should be done while holding this lock.
1556+
* generally the locks should be taken in order tcp_ses_lock before
1557+
* tcon->open_file_lock and that before file->file_info_lock since the
1558+
* structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
15501559
*/
15511560
GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
15521561

1553-
/*
1554-
* This lock protects the cifs_file->llist and cifs_file->flist
1555-
* list operations, and updates to some flags (cifs_file->invalidHandle)
1556-
* It will be moved to either use the tcon->stat_lock or equivalent later.
1557-
* If cifs_tcp_ses_lock and the lock below are both needed to be held, then
1558-
* the cifs_tcp_ses_lock must be grabbed first and released last.
1559-
*/
1560-
GLOBAL_EXTERN spinlock_t cifs_file_list_lock;
1561-
15621562
#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
15631563
/* Outstanding dir notify requests */
15641564
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;

fs/cifs/cifssmb.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
9898
struct list_head *tmp1;
9999

100100
/* list all files open on tree connection and mark them invalid */
101-
spin_lock(&cifs_file_list_lock);
101+
spin_lock(&tcon->open_file_lock);
102102
list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
103103
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
104104
open_file->invalidHandle = true;
105105
open_file->oplock_break_cancelled = true;
106106
}
107-
spin_unlock(&cifs_file_list_lock);
107+
spin_unlock(&tcon->open_file_lock);
108108
/*
109109
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
110110
* to this tcon.

fs/cifs/file.c

+39-27
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
305305
cfile->tlink = cifs_get_tlink(tlink);
306306
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
307307
mutex_init(&cfile->fh_mutex);
308+
spin_lock_init(&cfile->file_info_lock);
308309

309310
cifs_sb_active(inode->i_sb);
310311

@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
317318
oplock = 0;
318319
}
319320

320-
spin_lock(&cifs_file_list_lock);
321+
spin_lock(&tcon->open_file_lock);
321322
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
322323
oplock = fid->pending_open->oplock;
323324
list_del(&fid->pending_open->olist);
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
326327
server->ops->set_fid(cfile, fid, oplock);
327328

328329
list_add(&cfile->tlist, &tcon->openFileList);
330+
329331
/* if readable file instance put first in list*/
330332
if (file->f_mode & FMODE_READ)
331333
list_add(&cfile->flist, &cinode->openFileList);
332334
else
333335
list_add_tail(&cfile->flist, &cinode->openFileList);
334-
spin_unlock(&cifs_file_list_lock);
336+
spin_unlock(&tcon->open_file_lock);
335337

336338
if (fid->purge_cache)
337339
cifs_zap_mapping(inode);
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
343345
struct cifsFileInfo *
344346
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
345347
{
346-
spin_lock(&cifs_file_list_lock);
348+
spin_lock(&cifs_file->file_info_lock);
347349
cifsFileInfo_get_locked(cifs_file);
348-
spin_unlock(&cifs_file_list_lock);
350+
spin_unlock(&cifs_file->file_info_lock);
349351
return cifs_file;
350352
}
351353

352354
/*
353355
* Release a reference on the file private data. This may involve closing
354356
* the filehandle out on the server. Must be called without holding
355-
* cifs_file_list_lock.
357+
* tcon->open_file_lock and cifs_file->file_info_lock.
356358
*/
357359
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
358360
{
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
367369
struct cifs_pending_open open;
368370
bool oplock_break_cancelled;
369371

370-
spin_lock(&cifs_file_list_lock);
372+
spin_lock(&tcon->open_file_lock);
373+
374+
spin_lock(&cifs_file->file_info_lock);
371375
if (--cifs_file->count > 0) {
372-
spin_unlock(&cifs_file_list_lock);
376+
spin_unlock(&cifs_file->file_info_lock);
377+
spin_unlock(&tcon->open_file_lock);
373378
return;
374379
}
380+
spin_unlock(&cifs_file->file_info_lock);
375381

376382
if (server->ops->get_lease_key)
377383
server->ops->get_lease_key(inode, &fid);
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
395401
set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
396402
cifs_set_oplock_level(cifsi, 0);
397403
}
398-
spin_unlock(&cifs_file_list_lock);
404+
405+
spin_unlock(&tcon->open_file_lock);
399406

400407
oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
401408

@@ -772,10 +779,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
772779
server = tcon->ses->server;
773780

774781
cifs_dbg(FYI, "Freeing private data in close dir\n");
775-
spin_lock(&cifs_file_list_lock);
782+
spin_lock(&cfile->file_info_lock);
776783
if (server->ops->dir_needs_close(cfile)) {
777784
cfile->invalidHandle = true;
778-
spin_unlock(&cifs_file_list_lock);
785+
spin_unlock(&cfile->file_info_lock);
779786
if (server->ops->close_dir)
780787
rc = server->ops->close_dir(xid, tcon, &cfile->fid);
781788
else
@@ -784,7 +791,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
784791
/* not much we can do if it fails anyway, ignore rc */
785792
rc = 0;
786793
} else
787-
spin_unlock(&cifs_file_list_lock);
794+
spin_unlock(&cfile->file_info_lock);
788795

789796
buf = cfile->srch_inf.ntwrk_buf_start;
790797
if (buf) {
@@ -1728,12 +1735,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
17281735
{
17291736
struct cifsFileInfo *open_file = NULL;
17301737
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
1738+
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
17311739

17321740
/* only filter by fsuid on multiuser mounts */
17331741
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
17341742
fsuid_only = false;
17351743

1736-
spin_lock(&cifs_file_list_lock);
1744+
spin_lock(&tcon->open_file_lock);
17371745
/* we could simply get the first_list_entry since write-only entries
17381746
are always at the end of the list but since the first entry might
17391747
have a close pending, we go through the whole list */
@@ -1744,16 +1752,16 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
17441752
if (!open_file->invalidHandle) {
17451753
/* found a good file */
17461754
/* lock it so it will not be closed on us */
1747-
cifsFileInfo_get_locked(open_file);
1748-
spin_unlock(&cifs_file_list_lock);
1755+
cifsFileInfo_get(open_file);
1756+
spin_unlock(&tcon->open_file_lock);
17491757
return open_file;
17501758
} /* else might as well continue, and look for
17511759
another, or simply have the caller reopen it
17521760
again rather than trying to fix this handle */
17531761
} else /* write only file */
17541762
break; /* write only files are last so must be done */
17551763
}
1756-
spin_unlock(&cifs_file_list_lock);
1764+
spin_unlock(&tcon->open_file_lock);
17571765
return NULL;
17581766
}
17591767

@@ -1762,6 +1770,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
17621770
{
17631771
struct cifsFileInfo *open_file, *inv_file = NULL;
17641772
struct cifs_sb_info *cifs_sb;
1773+
struct cifs_tcon *tcon;
17651774
bool any_available = false;
17661775
int rc;
17671776
unsigned int refind = 0;
@@ -1777,15 +1786,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
17771786
}
17781787

17791788
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
1789+
tcon = cifs_sb_master_tcon(cifs_sb);
17801790

17811791
/* only filter by fsuid on multiuser mounts */
17821792
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
17831793
fsuid_only = false;
17841794

1785-
spin_lock(&cifs_file_list_lock);
1795+
spin_lock(&tcon->open_file_lock);
17861796
refind_writable:
17871797
if (refind > MAX_REOPEN_ATT) {
1788-
spin_unlock(&cifs_file_list_lock);
1798+
spin_unlock(&tcon->open_file_lock);
17891799
return NULL;
17901800
}
17911801
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
@@ -1796,8 +1806,8 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
17961806
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
17971807
if (!open_file->invalidHandle) {
17981808
/* found a good writable file */
1799-
cifsFileInfo_get_locked(open_file);
1800-
spin_unlock(&cifs_file_list_lock);
1809+
cifsFileInfo_get(open_file);
1810+
spin_unlock(&tcon->open_file_lock);
18011811
return open_file;
18021812
} else {
18031813
if (!inv_file)
@@ -1813,24 +1823,24 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
18131823

18141824
if (inv_file) {
18151825
any_available = false;
1816-
cifsFileInfo_get_locked(inv_file);
1826+
cifsFileInfo_get(inv_file);
18171827
}
18181828

1819-
spin_unlock(&cifs_file_list_lock);
1829+
spin_unlock(&tcon->open_file_lock);
18201830

18211831
if (inv_file) {
18221832
rc = cifs_reopen_file(inv_file, false);
18231833
if (!rc)
18241834
return inv_file;
18251835
else {
1826-
spin_lock(&cifs_file_list_lock);
1836+
spin_lock(&tcon->open_file_lock);
18271837
list_move_tail(&inv_file->flist,
18281838
&cifs_inode->openFileList);
1829-
spin_unlock(&cifs_file_list_lock);
1839+
spin_unlock(&tcon->open_file_lock);
18301840
cifsFileInfo_put(inv_file);
1831-
spin_lock(&cifs_file_list_lock);
18321841
++refind;
18331842
inv_file = NULL;
1843+
spin_lock(&tcon->open_file_lock);
18341844
goto refind_writable;
18351845
}
18361846
}
@@ -3612,15 +3622,17 @@ static int cifs_readpage(struct file *file, struct page *page)
36123622
static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
36133623
{
36143624
struct cifsFileInfo *open_file;
3625+
struct cifs_tcon *tcon =
3626+
cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
36153627

3616-
spin_lock(&cifs_file_list_lock);
3628+
spin_lock(&tcon->open_file_lock);
36173629
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
36183630
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
3619-
spin_unlock(&cifs_file_list_lock);
3631+
spin_unlock(&tcon->open_file_lock);
36203632
return 1;
36213633
}
36223634
}
3623-
spin_unlock(&cifs_file_list_lock);
3635+
spin_unlock(&tcon->open_file_lock);
36243636
return 0;
36253637
}
36263638

fs/cifs/misc.c

+8-7
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ tconInfoAlloc(void)
120120
++ret_buf->tc_count;
121121
INIT_LIST_HEAD(&ret_buf->openFileList);
122122
INIT_LIST_HEAD(&ret_buf->tcon_list);
123+
spin_lock_init(&ret_buf->open_file_lock);
123124
#ifdef CONFIG_CIFS_STATS
124125
spin_lock_init(&ret_buf->stat_lock);
125126
#endif
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
465466
continue;
466467

467468
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
468-
spin_lock(&cifs_file_list_lock);
469+
spin_lock(&tcon->open_file_lock);
469470
list_for_each(tmp2, &tcon->openFileList) {
470471
netfile = list_entry(tmp2, struct cifsFileInfo,
471472
tlist);
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
495496
&netfile->oplock_break);
496497
netfile->oplock_break_cancelled = false;
497498

498-
spin_unlock(&cifs_file_list_lock);
499+
spin_unlock(&tcon->open_file_lock);
499500
spin_unlock(&cifs_tcp_ses_lock);
500501
return true;
501502
}
502-
spin_unlock(&cifs_file_list_lock);
503+
spin_unlock(&tcon->open_file_lock);
503504
spin_unlock(&cifs_tcp_ses_lock);
504505
cifs_dbg(FYI, "No matching file for oplock break\n");
505506
return true;
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
613614
void
614615
cifs_del_pending_open(struct cifs_pending_open *open)
615616
{
616-
spin_lock(&cifs_file_list_lock);
617+
spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
617618
list_del(&open->olist);
618-
spin_unlock(&cifs_file_list_lock);
619+
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
619620
}
620621

621622
void
@@ -635,7 +636,7 @@ void
635636
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
636637
struct cifs_pending_open *open)
637638
{
638-
spin_lock(&cifs_file_list_lock);
639+
spin_lock(&tlink_tcon(tlink)->open_file_lock);
639640
cifs_add_pending_open_locked(fid, tlink, open);
640-
spin_unlock(&cifs_file_list_lock);
641+
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
641642
}

0 commit comments

Comments
 (0)