Skip to content

Commit d7d7a66

Browse files
sprasad-microsoftSteve French
authored andcommitted
cifs: avoid use of global locks for high contention data
During analysis of multichannel perf, it was seen that the global locks cifs_tcp_ses_lock and GlobalMid_Lock, which were shared between various data structures were causing a lot of contention points. With this change, we're breaking down the use of these locks by introducing new locks at more granular levels. i.e. server->srv_lock, ses->ses_lock and tcon->tc_lock to protect the unprotected fields of server, session and tcon structs; and server->mid_lock to protect mid related lists and entries at server level. Signed-off-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 1bfa25e commit d7d7a66

File tree

13 files changed

+336
-243
lines changed

13 files changed

+336
-243
lines changed

fs/cifs/cifs_debug.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
5555
return;
5656

5757
cifs_dbg(VFS, "Dump pending requests:\n");
58-
spin_lock(&GlobalMid_Lock);
58+
spin_lock(&server->mid_lock);
5959
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
6060
cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
6161
mid_entry->mid_state,
@@ -78,7 +78,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
7878
mid_entry->resp_buf, 62);
7979
}
8080
}
81-
spin_unlock(&GlobalMid_Lock);
81+
spin_unlock(&server->mid_lock);
8282
#endif /* CONFIG_CIFS_DEBUG2 */
8383
}
8484

@@ -463,7 +463,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
463463
seq_printf(m, "\n\t\t[NONE]");
464464

465465
seq_puts(m, "\n\n\tMIDs: ");
466-
spin_lock(&GlobalMid_Lock);
466+
spin_lock(&server->mid_lock);
467467
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
468468
seq_printf(m, "\n\tState: %d com: %d pid:"
469469
" %d cbdata: %p mid %llu\n",
@@ -473,7 +473,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
473473
mid_entry->callback_data,
474474
mid_entry->mid);
475475
}
476-
spin_unlock(&GlobalMid_Lock);
476+
spin_unlock(&server->mid_lock);
477477
seq_printf(m, "\n--\n");
478478
}
479479
if (c == 0)

fs/cifs/cifsencrypt.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,13 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
141141
if ((cifs_pdu == NULL) || (server == NULL))
142142
return -EINVAL;
143143

144-
spin_lock(&cifs_tcp_ses_lock);
144+
spin_lock(&server->srv_lock);
145145
if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
146146
server->tcpStatus == CifsNeedNegotiate) {
147-
spin_unlock(&cifs_tcp_ses_lock);
147+
spin_unlock(&server->srv_lock);
148148
return rc;
149149
}
150-
spin_unlock(&cifs_tcp_ses_lock);
150+
spin_unlock(&server->srv_lock);
151151

152152
if (!server->session_estab) {
153153
memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);

fs/cifs/cifsfs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,14 +731,17 @@ static void cifs_umount_begin(struct super_block *sb)
731731
tcon = cifs_sb_master_tcon(cifs_sb);
732732

733733
spin_lock(&cifs_tcp_ses_lock);
734+
spin_lock(&tcon->tc_lock);
734735
if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) {
735736
/* we have other mounts to same share or we have
736737
already tried to force umount this and woken up
737738
all waiting network requests, nothing to do */
739+
spin_unlock(&tcon->tc_lock);
738740
spin_unlock(&cifs_tcp_ses_lock);
739741
return;
740742
} else if (tcon->tc_count == 1)
741743
tcon->status = TID_EXITING;
744+
spin_unlock(&tcon->tc_lock);
742745
spin_unlock(&cifs_tcp_ses_lock);
743746

744747
/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */

fs/cifs/cifsglob.h

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ inc_rfc1001_len(void *buf, int count)
605605
struct TCP_Server_Info {
606606
struct list_head tcp_ses_list;
607607
struct list_head smb_ses_list;
608+
spinlock_t srv_lock; /* protect anything here that is not protected */
608609
__u64 conn_id; /* connection identifier (useful for debugging) */
609610
int srv_count; /* reference counter */
610611
/* 15 character server name + 0x20 16th byte indicating type = srv */
@@ -622,6 +623,7 @@ struct TCP_Server_Info {
622623
#endif
623624
wait_queue_head_t response_q;
624625
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
626+
spinlock_t mid_lock; /* protect mid queue and it's entries */
625627
struct list_head pending_mid_q;
626628
bool noblocksnd; /* use blocking sendmsg */
627629
bool noautotune; /* do not autotune send buf sizes */
@@ -1008,6 +1010,7 @@ struct cifs_ses {
10081010
struct list_head rlist; /* reconnect list */
10091011
struct list_head tcon_list;
10101012
struct cifs_tcon *tcon_ipc;
1013+
spinlock_t ses_lock; /* protect anything here that is not protected */
10111014
struct mutex session_mutex;
10121015
struct TCP_Server_Info *server; /* pointer to server info */
10131016
int ses_count; /* reference counter */
@@ -1169,6 +1172,7 @@ struct cifs_tcon {
11691172
struct list_head tcon_list;
11701173
int tc_count;
11711174
struct list_head rlist; /* reconnect list */
1175+
spinlock_t tc_lock; /* protect anything here that is not protected */
11721176
atomic_t num_local_opens; /* num of all opens including disconnected */
11731177
atomic_t num_remote_opens; /* num of all network opens on server */
11741178
struct list_head openFileList;
@@ -1899,33 +1903,78 @@ require use of the stronger protocol */
18991903
*/
19001904

19011905
/****************************************************************************
1902-
* Locking notes. All updates to global variables and lists should be
1903-
* protected by spinlocks or semaphores.
1906+
* Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according
1907+
* to the locking order. i.e. if two locks are to be held together, the lock that
1908+
* appears higher in this list needs to be taken before the other.
19041909
*
1905-
* Spinlocks
1906-
* ---------
1907-
* GlobalMid_Lock protects:
1908-
* list operations on pending_mid_q and oplockQ
1909-
* updates to XID counters, multiplex id and SMB sequence numbers
1910-
* list operations on global DnotifyReqList
1911-
* updates to ses->status and TCP_Server_Info->tcpStatus
1912-
* updates to server->CurrentMid
1913-
* tcp_ses_lock protects:
1914-
* list operations on tcp and SMB session lists
1915-
* tcon->open_file_lock protects the list of open files hanging off the tcon
1916-
* inode->open_file_lock protects the openFileList hanging off the inode
1917-
* cfile->file_info_lock protects counters and fields in cifs file struct
1918-
* f_owner.lock protects certain per file struct operations
1919-
* mapping->page_lock protects certain per page operations
1910+
* If you hold a lock that is lower in this list, and you need to take a higher lock
1911+
* (or if you think that one of the functions that you're calling may need to), first
1912+
* drop the lock you hold, pick up the higher lock, then the lower one. This will
1913+
* ensure that locks are picked up only in one direction in the below table
1914+
* (top to bottom).
19201915
*
1921-
* Note that the cifs_tcon.open_file_lock should be taken before
1922-
* not after the cifsInodeInfo.open_file_lock
1916+
* Also, if you expect a function to be called with a lock held, explicitly document
1917+
* this in the comments on top of your function definition.
19231918
*
1924-
* Semaphores
1925-
* ----------
1926-
* cifsInodeInfo->lock_sem protects:
1927-
* the list of locks held by the inode
1919+
* And also, try to keep the critical sections (lock hold time) to be as minimal as
1920+
* possible. Blocking / calling other functions with a lock held always increase
1921+
* the risk of a possible deadlock.
19281922
*
1923+
* Following this rule will avoid unnecessary deadlocks, which can get really hard to
1924+
* debug. Also, any new lock that you introduce, please add to this list in the correct
1925+
* order.
1926+
*
1927+
* Please populate this list whenever you introduce new locks in your changes. Or in
1928+
* case I've missed some existing locks. Please ensure that it's added in the list
1929+
* based on the locking order expected.
1930+
*
1931+
* =====================================================================================
1932+
* Lock Protects Initialization fn
1933+
* =====================================================================================
1934+
* vol_list_lock
1935+
* vol_info->ctx_lock vol_info->ctx
1936+
* cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb
1937+
* TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session
1938+
* reconnect_mutex
1939+
* TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session
1940+
* cifs_ses->session_mutex cifs_ses sesInfoAlloc
1941+
* cifs_tcon
1942+
* cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc
1943+
* cifs_tcon->pending_opens
1944+
* cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc
1945+
* cifs_tcon->bytes_written
1946+
* cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc
1947+
* GlobalMid_Lock GlobalMaxActiveXid init_cifs
1948+
* GlobalCurrentXid
1949+
* GlobalTotalActiveXid
1950+
* TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change)
1951+
* TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
1952+
* ->CurrentMid
1953+
* (any changes in mid_q_entry fields)
1954+
* TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session
1955+
* ->credits
1956+
* ->echo_credits
1957+
* ->oplock_credits
1958+
* ->reconnect_instance
1959+
* cifs_ses->ses_lock (anything that is not protected by another lock and can change)
1960+
* cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc
1961+
* ->iface_count
1962+
* ->iface_last_update
1963+
* cifs_ses->chan_lock cifs_ses->chans
1964+
* ->chans_need_reconnect
1965+
* ->chans_in_reconnect
1966+
* cifs_tcon->tc_lock (anything that is not protected by another lock and can change)
1967+
* cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode
1968+
* cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc
1969+
* cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once
1970+
* ->can_cache_brlcks
1971+
* cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc
1972+
* cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc
1973+
* cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo
1974+
* cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo
1975+
* ->invalidHandle initiate_cifs_search
1976+
* ->oplock_break_cancelled
1977+
* cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc
19291978
****************************************************************************/
19301979

19311980
#ifdef DECLARE_GLOBALS_HERE
@@ -1946,9 +1995,7 @@ extern struct list_head cifs_tcp_ses_list;
19461995
/*
19471996
* This lock protects the cifs_tcp_ses_list, the list of smb sessions per
19481997
* tcp session, and the list of tcon's per smb session. It also protects
1949-
* the reference counters for the server, smb session, and tcon. It also
1950-
* protects some fields in the TCP_Server_Info struct such as dstaddr. Finally,
1951-
* changes to the tcon->tidStatus should be done while holding this lock.
1998+
* the reference counters for the server, smb session, and tcon.
19521999
* generally the locks should be taken in order tcp_ses_lock before
19532000
* tcon->open_file_lock and that before file->file_info_lock since the
19542001
* structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file

fs/cifs/cifssmb.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
7474
struct list_head *tmp1;
7575

7676
/* only send once per connect */
77-
spin_lock(&cifs_tcp_ses_lock);
77+
spin_lock(&tcon->ses->ses_lock);
7878
if ((tcon->ses->ses_status != SES_GOOD) || (tcon->status != TID_NEED_RECON)) {
79-
spin_unlock(&cifs_tcp_ses_lock);
79+
spin_unlock(&tcon->ses->ses_lock);
8080
return;
8181
}
8282
tcon->status = TID_IN_FILES_INVALIDATE;
83-
spin_unlock(&cifs_tcp_ses_lock);
83+
spin_unlock(&tcon->ses->ses_lock);
8484

8585
/* list all files open on tree connection and mark them invalid */
8686
spin_lock(&tcon->open_file_lock);
@@ -98,10 +98,10 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
9898
memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
9999
mutex_unlock(&tcon->crfid.fid_mutex);
100100

101-
spin_lock(&cifs_tcp_ses_lock);
101+
spin_lock(&tcon->tc_lock);
102102
if (tcon->status == TID_IN_FILES_INVALIDATE)
103103
tcon->status = TID_NEED_TCON;
104-
spin_unlock(&cifs_tcp_ses_lock);
104+
spin_unlock(&tcon->tc_lock);
105105

106106
/*
107107
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
@@ -134,18 +134,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
134134
* only tree disconnect, open, and write, (and ulogoff which does not
135135
* have tcon) are allowed as we start force umount
136136
*/
137-
spin_lock(&cifs_tcp_ses_lock);
137+
spin_lock(&tcon->tc_lock);
138138
if (tcon->status == TID_EXITING) {
139139
if (smb_command != SMB_COM_WRITE_ANDX &&
140140
smb_command != SMB_COM_OPEN_ANDX &&
141141
smb_command != SMB_COM_TREE_DISCONNECT) {
142-
spin_unlock(&cifs_tcp_ses_lock);
142+
spin_unlock(&tcon->tc_lock);
143143
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
144144
smb_command);
145145
return -ENODEV;
146146
}
147147
}
148-
spin_unlock(&cifs_tcp_ses_lock);
148+
spin_unlock(&tcon->tc_lock);
149149

150150
retries = server->nr_targets;
151151

@@ -165,12 +165,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
165165
}
166166

167167
/* are we still trying to reconnect? */
168-
spin_lock(&cifs_tcp_ses_lock);
168+
spin_lock(&server->srv_lock);
169169
if (server->tcpStatus != CifsNeedReconnect) {
170-
spin_unlock(&cifs_tcp_ses_lock);
170+
spin_unlock(&server->srv_lock);
171171
break;
172172
}
173-
spin_unlock(&cifs_tcp_ses_lock);
173+
spin_unlock(&server->srv_lock);
174174

175175
if (retries && --retries)
176176
continue;
@@ -201,13 +201,13 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
201201
* and the server never sends an answer the socket will be closed
202202
* and tcpStatus set to reconnect.
203203
*/
204-
spin_lock(&cifs_tcp_ses_lock);
204+
spin_lock(&server->srv_lock);
205205
if (server->tcpStatus == CifsNeedReconnect) {
206-
spin_unlock(&cifs_tcp_ses_lock);
206+
spin_unlock(&server->srv_lock);
207207
rc = -EHOSTDOWN;
208208
goto out;
209209
}
210-
spin_unlock(&cifs_tcp_ses_lock);
210+
spin_unlock(&server->srv_lock);
211211

212212
/*
213213
* need to prevent multiple threads trying to simultaneously

0 commit comments

Comments
 (0)