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)
committerSteve French <stfrench@microsoft.com>
Thu, 9 Nov 2023 16:25:19 +0000 (10:25 -0600)
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>
fs/smb/client/sess.c
fs/smb/client/smb2ops.c

index 336b64d93e41cec9c45f0bb75545e31515dcfa03..e716d046fb5f5bc7439028f333873c99df08aa4a 100644 (file)
@@ -303,6 +303,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);
@@ -321,6 +322,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);
@@ -334,6 +339,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;
@@ -360,6 +375,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",
@@ -382,6 +404,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 601e7a187f8767abe50cef465ccfa7d39f7595b4..a959ed2c9b22e44a59dc31950c373c2661cc425e 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;