Skip to content

Commit

Permalink
Merge tag '5.19-rc3-smb3-client-fixes' of git://git.samba.org/sfrench…
Browse files Browse the repository at this point in the history
…/cifs-2.6

Pull cifs client fixes from Steve French:
 "Fixes addressing important multichannel, and reconnect issues.

  Multichannel mounts when the server network interfaces changed, or ip
  addresses changed, uncovered problems, especially in reconnect, but
  the patches for this were held up until recently due to some lock
  conflicts that are now addressed.

  Included in this set of fixes:

   - three fixes relating to multichannel reconnect, dynamically
     adjusting the list of server interfaces to avoid problems during
     reconnect

   - a lock conflict fix related to the above

   - two important fixes for negotiate on secondary channels (null
     netname can unintentionally cause multichannel to be disabled to
     some servers)

   - a reconnect fix (reporting incorrect IP address in some cases)"

* tag '5.19-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: update cifs_ses::ip_addr after failover
  cifs: avoid deadlocks while updating iface
  cifs: periodically query network interfaces from server
  cifs: during reconnect, update interface if necessary
  cifs: change iface_list from array to sorted linked list
  smb3: use netname when available on secondary channels
  smb3: fix empty netname context on secondary channels
  • Loading branch information
torvalds committed Jun 26, 2022
2 parents 0840a79 + af3a6d1 commit 918c30d
Show file tree
Hide file tree
Showing 8 changed files with 366 additions and 139 deletions.
12 changes: 7 additions & 5 deletions fs/cifs/cifs_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
else if (iface->sockaddr.ss_family == AF_INET6)
seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
if (!iface->is_active)
seq_puts(m, "\t\t[for-cleanup]\n");
}

static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
Expand Down Expand Up @@ -221,6 +223,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct cifs_server_iface *iface;
int c, i, j;

seq_puts(m,
Expand Down Expand Up @@ -456,11 +459,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (ses->iface_count)
seq_printf(m, "\n\n\tServer interfaces: %zu",
ses->iface_count);
for (j = 0; j < ses->iface_count; j++) {
struct cifs_server_iface *iface;

iface = &ses->iface_list[j];
seq_printf(m, "\n\t%d)", j+1);
j = 0;
list_for_each_entry(iface, &ses->iface_list,
iface_head) {
seq_printf(m, "\n\t%d)", ++j);
cifs_dump_iface(m, iface);
if (is_ses_using_iface(ses, iface))
seq_puts(m, "\t\t[CONNECTED]\n");
Expand Down
58 changes: 57 additions & 1 deletion fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
#define SMB_DNS_RESOLVE_INTERVAL_MIN 120
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600

/* smb multichannel query server interfaces interval in seconds */
#define SMB_INTERFACE_POLL_INTERVAL 600

/* maximum number of PDUs in one compound */
#define MAX_COMPOUND 5

Expand Down Expand Up @@ -933,15 +936,67 @@ static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
#endif

struct cifs_server_iface {
struct list_head iface_head;
struct kref refcount;
size_t speed;
unsigned int rdma_capable : 1;
unsigned int rss_capable : 1;
unsigned int is_active : 1; /* unset if non existent */
struct sockaddr_storage sockaddr;
};

/* release iface when last ref is dropped */
static inline void
release_iface(struct kref *ref)
{
struct cifs_server_iface *iface = container_of(ref,
struct cifs_server_iface,
refcount);
list_del_init(&iface->iface_head);
kfree(iface);
}

/*
* compare two interfaces a and b
* return 0 if everything matches.
* return 1 if a has higher link speed, or rdma capable, or rss capable
* return -1 otherwise.
*/
static inline int
iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
{
int cmp_ret = 0;

WARN_ON(!a || !b);
if (a->speed == b->speed) {
if (a->rdma_capable == b->rdma_capable) {
if (a->rss_capable == b->rss_capable) {
cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
sizeof(a->sockaddr));
if (!cmp_ret)
return 0;
else if (cmp_ret > 0)
return 1;
else
return -1;
} else if (a->rss_capable > b->rss_capable)
return 1;
else
return -1;
} else if (a->rdma_capable > b->rdma_capable)
return 1;
else
return -1;
} else if (a->speed > b->speed)
return 1;
else
return -1;
}

struct cifs_chan {
unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
struct TCP_Server_Info *server;
struct cifs_server_iface *iface; /* interface in use */
__u8 signkey[SMB3_SIGN_KEY_SIZE];
};

Expand Down Expand Up @@ -993,7 +1048,7 @@ struct cifs_ses {
*/
spinlock_t iface_lock;
/* ========= begin: protected by iface_lock ======== */
struct cifs_server_iface *iface_list;
struct list_head iface_list;
size_t iface_count;
unsigned long iface_last_update; /* jiffies */
/* ========= end: protected by iface_lock ======== */
Expand Down Expand Up @@ -1203,6 +1258,7 @@ struct cifs_tcon {
#ifdef CONFIG_CIFS_DFS_UPCALL
struct list_head ulist; /* cache update list */
#endif
struct delayed_work query_interfaces; /* query interfaces workqueue job */
};

/*
Expand Down
7 changes: 7 additions & 0 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,13 @@ 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);
int
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon);

void extract_unc_hostname(const char *unc, const char **h, size_t *len);
int copy_path_name(char *dst, const char *src);
Expand Down
59 changes: 54 additions & 5 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,25 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
return rc;
}

static void smb2_query_server_interfaces(struct work_struct *work)
{
int rc;
struct cifs_tcon *tcon = container_of(work,
struct cifs_tcon,
query_interfaces.work);

/*
* query server network interfaces, in case they change
*/
rc = SMB3_request_interfaces(0, tcon);
if (rc) {
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
}

queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
(SMB_INTERFACE_POLL_INTERVAL * HZ));
}

static void cifs_resolve_server(struct work_struct *work)
{
Expand Down Expand Up @@ -217,7 +236,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{
struct TCP_Server_Info *pserver;
struct cifs_ses *ses;
struct cifs_ses *ses, *nses;
struct cifs_tcon *tcon;

/*
Expand All @@ -231,7 +250,20 @@ 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) {
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
/* check if iface is still active */
if (!cifs_chan_is_iface_active(ses, server)) {
/*
* HACK: drop the lock before calling
* cifs_chan_update_iface to avoid deadlock
*/
ses->ses_count++;
spin_unlock(&cifs_tcp_ses_lock);
cifs_chan_update_iface(ses, server);
spin_lock(&cifs_tcp_ses_lock);
ses->ses_count--;
}

spin_lock(&ses->chan_lock);
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
goto next_session;
Expand Down Expand Up @@ -1894,9 +1926,11 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
int i;

for (i = 1; i < chan_count; i++) {
spin_unlock(&ses->chan_lock);
if (ses->chans[i].iface) {
kref_put(&ses->chans[i].iface->refcount, release_iface);
ses->chans[i].iface = NULL;
}
cifs_put_tcp_session(ses->chans[i].server, 0);
spin_lock(&ses->chan_lock);
ses->chans[i].server = NULL;
}
}
Expand Down Expand Up @@ -2270,6 +2304,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
list_del_init(&tcon->tcon_list);
spin_unlock(&cifs_tcp_ses_lock);

/* cancel polling of interfaces */
cancel_delayed_work_sync(&tcon->query_interfaces);

if (tcon->use_witness) {
int rc;

Expand Down Expand Up @@ -2507,6 +2544,12 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
tcon->local_lease = ctx->local_lease;
INIT_LIST_HEAD(&tcon->pending_opens);

/* schedule query interfaces poll */
INIT_DELAYED_WORK(&tcon->query_interfaces,
smb2_query_server_interfaces);
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
(SMB_INTERFACE_POLL_INTERVAL * HZ));

spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list);
spin_unlock(&cifs_tcp_ses_lock);
Expand Down Expand Up @@ -3982,10 +4025,16 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct nls_table *nls_info)
{
int rc = -ENOSYS;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
bool is_binding = false;


spin_lock(&cifs_tcp_ses_lock);
if (server->dstaddr.ss_family == AF_INET6)
scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr);
else
scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr);

if (ses->ses_status != SES_GOOD &&
ses->ses_status != SES_NEW &&
ses->ses_status != SES_NEED_RECON) {
Expand Down
9 changes: 8 additions & 1 deletion fs/cifs/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ sesInfoAlloc(void)
INIT_LIST_HEAD(&ret_buf->tcon_list);
mutex_init(&ret_buf->session_mutex);
spin_lock_init(&ret_buf->iface_lock);
INIT_LIST_HEAD(&ret_buf->iface_list);
spin_lock_init(&ret_buf->chan_lock);
}
return ret_buf;
Expand All @@ -83,6 +84,8 @@ sesInfoAlloc(void)
void
sesInfoFree(struct cifs_ses *buf_to_free)
{
struct cifs_server_iface *iface = NULL, *niface = NULL;

if (buf_to_free == NULL) {
cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");
return;
Expand All @@ -96,7 +99,11 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
kfree_sensitive(buf_to_free->auth_key.response);
kfree(buf_to_free->iface_list);
spin_lock(&buf_to_free->iface_lock);
list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list,
iface_head)
kref_put(&iface->refcount, release_iface);
spin_unlock(&buf_to_free->iface_lock);
kfree_sensitive(buf_to_free);
}

Expand Down
Loading

0 comments on commit 918c30d

Please sign in to comment.