Skip to content

Commit

Permalink
cifs: during reconnect, update interface if necessary
Browse files Browse the repository at this point in the history
Going forward, the plan is to periodically query the server
for it's interfaces (when multichannel is enabled).

This change allows checking for inactive interfaces during
reconnect, and reconnect to a new interface if necessary.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
sprasad-microsoft authored and Steve French committed Jun 23, 2022
1 parent aa45dad commit b54034a
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
5 changes: 5 additions & 0 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,11 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
bool
cifs_chan_needs_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server);
bool
cifs_chan_is_iface_active(struct cifs_ses *ses,
struct TCP_Server_Info *server);
int
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);

void extract_unc_hostname(const char *unc, const char **h, size_t *len);
int copy_path_name(char *dst, const char *src);
Expand Down
4 changes: 4 additions & 0 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ 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) {
/* check if iface is still active */
if (!cifs_chan_is_iface_active(ses, server))
cifs_chan_update_iface(ses, server);

spin_lock(&ses->chan_lock);
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
goto next_session;
Expand Down
79 changes: 79 additions & 0 deletions fs/cifs/sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
}

bool
cifs_chan_is_iface_active(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);

return ses->chans[chan_index].iface &&
ses->chans[chan_index].iface->is_active;
}

/* returns number of channels added */
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{
Expand Down Expand Up @@ -244,6 +254,75 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
return new_chan_count - old_chan_count;
}

/*
* update the iface for the channel if necessary.
* will return 0 when iface is updated. 1 otherwise
* Must be called with chan_lock held.
*/
int
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
{
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
struct cifs_server_iface *iface = NULL;
struct cifs_server_iface *old_iface = NULL;
int rc = 0;

/* primary channel. This can never go away */
if (!chan_index)
return 0;

if (ses->chans[chan_index].iface) {
old_iface = ses->chans[chan_index].iface;
if (old_iface->is_active)
return 1;
}

spin_lock(&ses->iface_lock);

/* then look for a new one */
list_for_each_entry(iface, &ses->iface_list, iface_head) {
if (!iface->is_active ||
(is_ses_using_iface(ses, iface) &&
!iface->rss_capable)) {
continue;
}
kref_get(&iface->refcount);
}

if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) {
rc = 1;
iface = NULL;
cifs_dbg(FYI, "unable to find a suitable iface\n");
}

ses->chans[chan_index].iface = iface;

/* now drop the ref to the current iface */
if (old_iface && iface) {
kref_put(&old_iface->refcount, release_iface);
cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
&old_iface->sockaddr,
&iface->sockaddr);
} else if (old_iface) {
kref_put(&old_iface->refcount, release_iface);
cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
&old_iface->sockaddr);
} else {
WARN_ON(!iface);
cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
}

spin_unlock(&ses->iface_lock);

/* No iface is found. if secondary chan, drop connection */
if (!iface && CIFS_SERVER_IS_CHAN(server)) {
cifs_put_tcp_session(server, false);
ses->chans[chan_index].server = NULL;
}

return rc;
}

/*
* If server is a channel of ses, return the corresponding enclosing
* cifs_chan otherwise return NULL.
Expand Down

0 comments on commit b54034a

Please sign in to comment.