cifs: account for primary channel in the interface list
authorShyam Prasad N <sprasad@microsoft.com>
Tue, 14 Mar 2023 11:14:58 +0000 (11:14 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 3 Dec 2023 06:33:09 +0000 (07:33 +0100)
[ Upstream commit fa1d0508bdd4a68c5e40f85f635712af8c12f180 ]

The refcounting of server interfaces should account
for the primary channel too. Although this is not
strictly necessary, doing so will account for the primary
channel in DebugData.

Cc: stable@vger.kernel.org
Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/sess.c
fs/smb/client/smb2ops.c

index 65545e6..80050e3 100644 (file)
@@ -288,6 +288,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
        struct cifs_server_iface *iface = NULL;
        struct cifs_server_iface *old_iface = NULL;
        struct cifs_server_iface *last_iface = NULL;
+       struct sockaddr_storage ss;
        int rc = 0;
 
        spin_lock(&ses->chan_lock);
@@ -306,6 +307,10 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
        }
        spin_unlock(&ses->chan_lock);
 
+       spin_lock(&server->srv_lock);
+       ss = server->dstaddr;
+       spin_unlock(&server->srv_lock);
+
        spin_lock(&ses->iface_lock);
        if (!ses->iface_count) {
                spin_unlock(&ses->iface_lock);
@@ -319,6 +324,16 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
 
        /* then look for a new one */
        list_for_each_entry(iface, &ses->iface_list, iface_head) {
+               if (!chan_index) {
+                       /* if we're trying to get the updated iface for primary channel */
+                       if (!cifs_match_ipaddr((struct sockaddr *) &ss,
+                                              (struct sockaddr *) &iface->sockaddr))
+                               continue;
+
+                       kref_get(&iface->refcount);
+                       break;
+               }
+
                /* do not mix rdma and non-rdma interfaces */
                if (iface->rdma_capable != server->rdma)
                        continue;
@@ -345,6 +360,13 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
                cifs_dbg(FYI, "unable to find a suitable iface\n");
        }
 
+       if (!chan_index && !iface) {
+               cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
+                        &ss);
+               spin_unlock(&ses->iface_lock);
+               return 0;
+       }
+
        /* now drop the ref to the current iface */
        if (old_iface && iface) {
                cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
@@ -367,6 +389,12 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
                        old_iface->weight_fulfilled--;
 
                kref_put(&old_iface->refcount, release_iface);
+       } else if (!chan_index) {
+               /* special case: update interface for primary channel */
+               cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
+                        &iface->sockaddr);
+               iface->num_channels++;
+               iface->weight_fulfilled++;
        } else {
                WARN_ON(!iface);
                cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
index 4af0085..0f8fa78 100644 (file)
@@ -756,6 +756,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
        unsigned int ret_data_len = 0;
        struct network_interface_info_ioctl_rsp *out_buf = NULL;
        struct cifs_ses *ses = tcon->ses;
+       struct TCP_Server_Info *pserver;
 
        /* do not query too frequently */
        if (ses->iface_last_update &&
@@ -780,6 +781,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
        if (rc)
                goto out;
 
+       /* check if iface is still active */
+       pserver = ses->chans[0].server;
+       if (pserver && !cifs_chan_is_iface_active(ses, pserver))
+               cifs_chan_update_iface(ses, pserver);
+
 out:
        kfree(out_buf);
        return rc;