cifs: reconnect only the connection and not smb session where possible

With the new per-channel bitmask for reconnect, we have an option to
reconnect the tcp session associated with the channel without reconnecting
the smb session. i.e. if there are still channels to operate on, we can
continue to use the smb session and tcon.

However, there are cases where it makes sense to reconnect the smb session
even when there are active channels underneath. For example for
SMB session expiry.

With this patch, we'll have an option to do either, and use the correct
option for specific cases.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Shyam Prasad N 2021-07-19 14:14:46 +00:00 коммит произвёл Steve French
Родитель 2e0fa298d1
Коммит 183eea2ee5
5 изменённых файлов: 52 добавлений и 35 удалений

Просмотреть файл

@ -973,6 +973,8 @@ struct cifs_ses {
((1UL << (ses)->chan_count) - 1)
#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \
((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \
((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses))
#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \
test_bit((index), &(ses)->chans_need_reconnect)

Просмотреть файл

@ -131,7 +131,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
struct smb_hdr *in_buf ,
struct smb_hdr *out_buf,
int *bytes_returned);
extern int cifs_reconnect(struct TCP_Server_Info *server);
extern int cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *);

Просмотреть файл

@ -1439,7 +1439,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
cifs_reconnect(server);
cifs_reconnect(server, true);
return -1;
}

Просмотреть файл

@ -166,8 +166,11 @@ static void cifs_resolve_server(struct work_struct *work)
* Mark all sessions and tcons for reconnect.
*
* @server needs to be previously set to CifsNeedReconnect.
*
*/
static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server)
static void
cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{
unsigned int num_sessions = 0;
struct cifs_ses *ses;
@ -193,13 +196,16 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
spin_lock(&ses->chan_lock);
if (cifs_chan_needs_reconnect(ses, server))
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
goto next_session;
cifs_chan_set_need_reconnect(ses, server);
if (mark_smb_session)
CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses);
else
cifs_chan_set_need_reconnect(ses, server);
/* If all channels need reconnect, then tcon needs reconnect */
if (!CIFS_ALL_CHANS_NEED_RECONNECT(ses))
if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
goto next_session;
num_sessions++;
@ -271,16 +277,16 @@ next_session:
static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
{
spin_lock(&GlobalMid_Lock);
spin_lock(&cifs_tcp_ses_lock);
server->nr_targets = num_targets;
if (server->tcpStatus == CifsExiting) {
/* the demux thread will exit normally next time through the loop */
spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
wake_up(&server->response_q);
return false;
}
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
@ -291,15 +297,21 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
* mark all smb sessions as reconnecting for tcp session
* reconnect tcp session
* wake up waiters on reconnection? - (not needed currently)
*
* if mark_smb_session is passed as true, unconditionally mark
* the smb session (and tcon) for reconnect as well. This value
* doesn't really matter for non-multichannel scenario.
*
*/
static int __cifs_reconnect(struct TCP_Server_Info *server)
static int __cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{
int rc = 0;
if (!cifs_tcp_ses_needs_reconnect(server, 1))
return 0;
cifs_mark_tcp_ses_conns_for_reconnect(server);
cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
do {
try_to_freeze();
@ -322,10 +334,10 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
} else {
atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1);
spin_lock(&GlobalMid_Lock);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex);
}
@ -394,7 +406,9 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
return rc;
}
static int reconnect_dfs_server(struct TCP_Server_Info *server)
static int
reconnect_dfs_server(struct TCP_Server_Info *server,
bool mark_smb_session)
{
int rc = 0;
const char *refpath = server->current_fullpath + 1;
@ -418,7 +432,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
return 0;
cifs_mark_tcp_ses_conns_for_reconnect(server);
cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
do {
try_to_freeze();
@ -439,10 +453,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
*/
atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1);
spin_lock(&GlobalMid_Lock);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex);
} while (server->tcpStatus == CifsNeedReconnect);
@ -460,22 +474,22 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
return rc;
}
int cifs_reconnect(struct TCP_Server_Info *server)
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{
/* If tcp session is not an dfs connection, then reconnect to last target server */
spin_lock(&cifs_tcp_ses_lock);
if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
spin_unlock(&cifs_tcp_ses_lock);
return __cifs_reconnect(server);
return __cifs_reconnect(server, mark_smb_session);
}
spin_unlock(&cifs_tcp_ses_lock);
return reconnect_dfs_server(server);
return reconnect_dfs_server(server, mark_smb_session);
}
#else
int cifs_reconnect(struct TCP_Server_Info *server)
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{
return __cifs_reconnect(server);
return __cifs_reconnect(server, mark_smb_session);
}
#endif
@ -563,7 +577,7 @@ server_unresponsive(struct TCP_Server_Info *server)
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
(3 * server->echo_interval) / HZ);
cifs_reconnect(server);
cifs_reconnect(server, false);
return true;
}
@ -599,7 +613,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
/* reconnect if no credits and no requests in flight */
if (zero_credits(server)) {
cifs_reconnect(server);
cifs_reconnect(server, false);
return -ECONNABORTED;
}
@ -614,7 +628,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
return -ESHUTDOWN;
if (server->tcpStatus == CifsNeedReconnect) {
cifs_reconnect(server);
cifs_reconnect(server, false);
return -ECONNABORTED;
}
@ -633,7 +647,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
if (length <= 0) {
cifs_dbg(FYI, "Received no data or error: %d\n", length);
cifs_reconnect(server);
cifs_reconnect(server, false);
return -ECONNABORTED;
}
}
@ -712,11 +726,11 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
* initialize frame).
*/
cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
cifs_reconnect(server);
cifs_reconnect(server, true);
break;
default:
cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
cifs_reconnect(server);
cifs_reconnect(server, true);
}
return false;
@ -889,7 +903,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
server->vals->header_preamble_size) {
cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server);
cifs_reconnect(server, true);
return -ECONNABORTED;
}
@ -936,7 +950,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
cifs_reconnect(server);
cifs_reconnect(server, true);
return -1;
}
@ -1040,7 +1054,7 @@ next_pdu:
server->vals->header_preamble_size) {
cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
server->pdu_size);
cifs_reconnect(server);
cifs_reconnect(server, true);
continue;
}
@ -1092,7 +1106,7 @@ next_pdu:
server->ops->is_status_io_timeout(buf)) {
num_io_timeout++;
if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
cifs_reconnect(server);
cifs_reconnect(server, false);
num_io_timeout = 0;
continue;
}

Просмотреть файл

@ -4810,7 +4810,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
if (!is_offloaded)
cifs_reconnect(server);
cifs_reconnect(server, true);
return -1;
}
@ -5223,13 +5223,13 @@ smb3_receive_transform(struct TCP_Server_Info *server,
sizeof(struct smb2_hdr)) {
cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
pdu_length);
cifs_reconnect(server);
cifs_reconnect(server, true);
return -ECONNABORTED;
}
if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
cifs_server_dbg(VFS, "Transform message is broken\n");
cifs_reconnect(server);
cifs_reconnect(server, true);
return -ECONNABORTED;
}