diff mbox series

[06/14] cifs: handle cases where a channel is closed

Message ID 20231030110020.45627-6-sprasad@microsoft.com
State New
Headers show
Series [01/14] cifs: print server capabilities in DebugData | expand

Commit Message

Shyam Prasad N Oct. 30, 2023, 11 a.m. UTC
From: Shyam Prasad N <sprasad@microsoft.com>

So far, SMB multichannel could only scale up, but not
scale down the number of channels. In this series of
patch, we now allow the client to deal with the case
of multichannel disabled on the server when the share
is mounted. With that change, we now need the ability
to scale down the channels.

This change allows the client to deal with cases of
missing channels more gracefully.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
 fs/smb/client/cifs_debug.c    |  5 +++++
 fs/smb/client/cifsglob.h      |  1 +
 fs/smb/client/cifsproto.h     |  2 +-
 fs/smb/client/connect.c       | 10 ++++++++--
 fs/smb/client/sess.c          | 25 +++++++++++++++++++++----
 fs/smb/client/smb2transport.c |  8 +++++++-
 6 files changed, 43 insertions(+), 8 deletions(-)

Comments

Steve French Nov. 1, 2023, 3:09 a.m. UTC | #1
This patch wouldn't merge due to a merge conflict here.  Am I missing
an earlier patch of yours before the series?

I have these in for-next:
d1ea8f36fa51 (HEAD -> for-next) cifs: force interface update before a
fresh session setup
1e27cedcaf4e (origin/for-next) cifs: do not reset chan_max if
multichannel is not supported at mount
e257df806ae0 cifs: display the endpoint IP details in DebugData
9d95130c9f78 cifs: reconnect helper should set reconnect for the right channel
7ac6866076bd smb: client: fix use-after-free in smb2_query_info_compound()
0779365fc10d smb: client: remove extra @chan_count check in __cifs_put_smb_ses()
4cf6e1101a25 cifs: add xid to query server interface call
52768695d36a cifs: print server capabilities in DebugData


--- fs/smb/client/connect.c
+++ fs/smb/client/connect.c
@@ -2037,7 +2041,9 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)

kref_put(&ses->chans[i].iface->refcount, release_iface);
                                ses->chans[i].iface = NULL;
                        }
-                       cifs_put_tcp_session(ses->chans[i].server, 0);
+
+                       if (ses->chans[i].server)
+                               cifs_put_tcp_session(ses->chans[i].server, 0);
                        ses->chans[i].server = NULL;
                }
        }

On Mon, Oct 30, 2023 at 6:00 AM <nspmangalore@gmail.com> wrote:
>
> From: Shyam Prasad N <sprasad@microsoft.com>
>
> So far, SMB multichannel could only scale up, but not
> scale down the number of channels. In this series of
> patch, we now allow the client to deal with the case
> of multichannel disabled on the server when the share
> is mounted. With that change, we now need the ability
> to scale down the channels.
>
> This change allows the client to deal with cases of
> missing channels more gracefully.
>
> Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> ---
>  fs/smb/client/cifs_debug.c    |  5 +++++
>  fs/smb/client/cifsglob.h      |  1 +
>  fs/smb/client/cifsproto.h     |  2 +-
>  fs/smb/client/connect.c       | 10 ++++++++--
>  fs/smb/client/sess.c          | 25 +++++++++++++++++++++----
>  fs/smb/client/smb2transport.c |  8 +++++++-
>  6 files changed, 43 insertions(+), 8 deletions(-)
>
> diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
> index a9dfecc397a8..9fca09539728 100644
> --- a/fs/smb/client/cifs_debug.c
> +++ b/fs/smb/client/cifs_debug.c
> @@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
>  {
>         struct TCP_Server_Info *server = chan->server;
>
> +       if (!server) {
> +               seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
> +               return;
> +       }
> +
>         seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
>                    "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
>                    "\n\t\tTCP status: %d Instance: %d"
> diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
> index 02082621d8e0..552ed441281a 100644
> --- a/fs/smb/client/cifsglob.h
> +++ b/fs/smb/client/cifsglob.h
> @@ -1050,6 +1050,7 @@ struct cifs_ses {
>         spinlock_t chan_lock;
>         /* ========= begin: protected by chan_lock ======== */
>  #define CIFS_MAX_CHANNELS 16
> +#define CIFS_INVAL_CHAN_INDEX (-1)
>  #define CIFS_ALL_CHANNELS_SET(ses)     \
>         ((1UL << (ses)->chan_count) - 1)
>  #define CIFS_ALL_CHANS_GOOD(ses)               \
> diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
> index 0c37eefa18a5..65c84b3d1a65 100644
> --- a/fs/smb/client/cifsproto.h
> +++ b/fs/smb/client/cifsproto.h
> @@ -616,7 +616,7 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
>  bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
>  void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
>
> -unsigned int
> +int
>  cifs_ses_get_chan_index(struct cifs_ses *ses,
>                         struct TCP_Server_Info *server);
>  void
> diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
> index 97c9a32cff36..8393977e21ee 100644
> --- a/fs/smb/client/connect.c
> +++ b/fs/smb/client/connect.c
> @@ -173,8 +173,12 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
>         list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
>                 spin_lock(&ses->chan_lock);
>                 for (i = 0; i < ses->chan_count; i++) {
> +                       if (!ses->chans[i].server)
> +                               continue;
> +
>                         spin_lock(&ses->chans[i].server->srv_lock);
> -                       ses->chans[i].server->tcpStatus = CifsNeedReconnect;
> +                       if (ses->chans[i].server->tcpStatus != CifsExiting)
> +                               ses->chans[i].server->tcpStatus = CifsNeedReconnect;
>                         spin_unlock(&ses->chans[i].server->srv_lock);
>                 }
>                 spin_unlock(&ses->chan_lock);
> @@ -2033,7 +2037,9 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
>                                 kref_put(&ses->chans[i].iface->refcount, release_iface);
>                                 ses->chans[i].iface = NULL;
>                         }
> -                       cifs_put_tcp_session(ses->chans[i].server, 0);
> +
> +                       if (ses->chans[i].server)
> +                               cifs_put_tcp_session(ses->chans[i].server, 0);
>                         ses->chans[i].server = NULL;
>                 }
>         }
> diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
> index c899b05c92f7..9d2228c2d7e5 100644
> --- a/fs/smb/client/sess.c
> +++ b/fs/smb/client/sess.c
> @@ -69,7 +69,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
>
>  /* channel helper functions. assumed that chan_lock is held by caller. */
>
> -unsigned int
> +int
>  cifs_ses_get_chan_index(struct cifs_ses *ses,
>                         struct TCP_Server_Info *server)
>  {
> @@ -85,14 +85,16 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
>                 cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
>                          server->conn_id);
>         WARN_ON(1);
> -       return 0;
> +       return CIFS_INVAL_CHAN_INDEX;
>  }
>
>  void
>  cifs_chan_set_in_reconnect(struct cifs_ses *ses,
>                              struct TCP_Server_Info *server)
>  {
> -       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return;
>
>         ses->chans[chan_index].in_reconnect = true;
>  }
> @@ -102,6 +104,8 @@ cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
>                              struct TCP_Server_Info *server)
>  {
>         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return;
>
>         ses->chans[chan_index].in_reconnect = false;
>  }
> @@ -111,6 +115,8 @@ cifs_chan_in_reconnect(struct cifs_ses *ses,
>                           struct TCP_Server_Info *server)
>  {
>         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return true;    /* err on the safer side */
>
>         return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
>  }
> @@ -120,6 +126,8 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses,
>                              struct TCP_Server_Info *server)
>  {
>         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return;
>
>         set_bit(chan_index, &ses->chans_need_reconnect);
>         cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
> @@ -131,6 +139,8 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
>                                struct TCP_Server_Info *server)
>  {
>         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return;
>
>         clear_bit(chan_index, &ses->chans_need_reconnect);
>         cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
> @@ -142,6 +152,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
>                           struct TCP_Server_Info *server)
>  {
>         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return true;    /* err on the safer side */
>
>         return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
>  }
> @@ -151,6 +163,8 @@ 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);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return true;    /* err on the safer side */
>
>         return ses->chans[chan_index].iface &&
>                 ses->chans[chan_index].iface->is_active;
> @@ -269,7 +283,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
>
>         spin_lock(&ses->chan_lock);
>         chan_index = cifs_ses_get_chan_index(ses, server);
> -       if (!chan_index) {
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
>                 spin_unlock(&ses->chan_lock);
>                 return 0;
>         }
> @@ -319,6 +333,9 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
>
>         spin_lock(&ses->chan_lock);
>         chan_index = cifs_ses_get_chan_index(ses, server);
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> +               return 0;
> +
>         ses->chans[chan_index].iface = iface;
>
>         /* No iface is found. if secondary chan, drop connection */
> diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
> index 23c50ed7d4b5..84ea67301303 100644
> --- a/fs/smb/client/smb2transport.c
> +++ b/fs/smb/client/smb2transport.c
> @@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
>                       ses->ses_status == SES_GOOD);
>
>         chan_index = cifs_ses_get_chan_index(ses, server);
> -       /* TODO: introduce ref counting for channels when the can be freed */
> +       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
> +               spin_unlock(&ses->chan_lock);
> +               spin_unlock(&ses->ses_lock);
> +
> +               return -EINVAL;
> +       }
> +
>         spin_unlock(&ses->chan_lock);
>         spin_unlock(&ses->ses_lock);
>
> --
> 2.34.1
>
Shyam Prasad N Nov. 2, 2023, 12:26 p.m. UTC | #2
On Wed, Nov 1, 2023 at 8:39 AM Steve French <smfrench@gmail.com> wrote:
>
> This patch wouldn't merge due to a merge conflict here.  Am I missing
> an earlier patch of yours before the series?
>

I think the conflict is with:
0779365fc10d smb: client: remove extra @chan_count check in __cifs_put_smb_ses()

Let me resolve the conflict and give you an updated patch.

> I have these in for-next:
> d1ea8f36fa51 (HEAD -> for-next) cifs: force interface update before a
> fresh session setup
> 1e27cedcaf4e (origin/for-next) cifs: do not reset chan_max if
> multichannel is not supported at mount
> e257df806ae0 cifs: display the endpoint IP details in DebugData
> 9d95130c9f78 cifs: reconnect helper should set reconnect for the right channel
> 7ac6866076bd smb: client: fix use-after-free in smb2_query_info_compound()
> 0779365fc10d smb: client: remove extra @chan_count check in __cifs_put_smb_ses()
> 4cf6e1101a25 cifs: add xid to query server interface call
> 52768695d36a cifs: print server capabilities in DebugData
>
>
> --- fs/smb/client/connect.c
> +++ fs/smb/client/connect.c
> @@ -2037,7 +2041,9 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
>
> kref_put(&ses->chans[i].iface->refcount, release_iface);
>                                 ses->chans[i].iface = NULL;
>                         }
> -                       cifs_put_tcp_session(ses->chans[i].server, 0);
> +
> +                       if (ses->chans[i].server)
> +                               cifs_put_tcp_session(ses->chans[i].server, 0);
>                         ses->chans[i].server = NULL;
>                 }
>         }
>
> On Mon, Oct 30, 2023 at 6:00 AM <nspmangalore@gmail.com> wrote:
> >
> > From: Shyam Prasad N <sprasad@microsoft.com>
> >
> > So far, SMB multichannel could only scale up, but not
> > scale down the number of channels. In this series of
> > patch, we now allow the client to deal with the case
> > of multichannel disabled on the server when the share
> > is mounted. With that change, we now need the ability
> > to scale down the channels.
> >
> > This change allows the client to deal with cases of
> > missing channels more gracefully.
> >
> > Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> > ---
> >  fs/smb/client/cifs_debug.c    |  5 +++++
> >  fs/smb/client/cifsglob.h      |  1 +
> >  fs/smb/client/cifsproto.h     |  2 +-
> >  fs/smb/client/connect.c       | 10 ++++++++--
> >  fs/smb/client/sess.c          | 25 +++++++++++++++++++++----
> >  fs/smb/client/smb2transport.c |  8 +++++++-
> >  6 files changed, 43 insertions(+), 8 deletions(-)
> >
> > diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
> > index a9dfecc397a8..9fca09539728 100644
> > --- a/fs/smb/client/cifs_debug.c
> > +++ b/fs/smb/client/cifs_debug.c
> > @@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
> >  {
> >         struct TCP_Server_Info *server = chan->server;
> >
> > +       if (!server) {
> > +               seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
> > +               return;
> > +       }
> > +
> >         seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
> >                    "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
> >                    "\n\t\tTCP status: %d Instance: %d"
> > diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
> > index 02082621d8e0..552ed441281a 100644
> > --- a/fs/smb/client/cifsglob.h
> > +++ b/fs/smb/client/cifsglob.h
> > @@ -1050,6 +1050,7 @@ struct cifs_ses {
> >         spinlock_t chan_lock;
> >         /* ========= begin: protected by chan_lock ======== */
> >  #define CIFS_MAX_CHANNELS 16
> > +#define CIFS_INVAL_CHAN_INDEX (-1)
> >  #define CIFS_ALL_CHANNELS_SET(ses)     \
> >         ((1UL << (ses)->chan_count) - 1)
> >  #define CIFS_ALL_CHANS_GOOD(ses)               \
> > diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
> > index 0c37eefa18a5..65c84b3d1a65 100644
> > --- a/fs/smb/client/cifsproto.h
> > +++ b/fs/smb/client/cifsproto.h
> > @@ -616,7 +616,7 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
> >  bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
> >  void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
> >
> > -unsigned int
> > +int
> >  cifs_ses_get_chan_index(struct cifs_ses *ses,
> >                         struct TCP_Server_Info *server);
> >  void
> > diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
> > index 97c9a32cff36..8393977e21ee 100644
> > --- a/fs/smb/client/connect.c
> > +++ b/fs/smb/client/connect.c
> > @@ -173,8 +173,12 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
> >         list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
> >                 spin_lock(&ses->chan_lock);
> >                 for (i = 0; i < ses->chan_count; i++) {
> > +                       if (!ses->chans[i].server)
> > +                               continue;
> > +
> >                         spin_lock(&ses->chans[i].server->srv_lock);
> > -                       ses->chans[i].server->tcpStatus = CifsNeedReconnect;
> > +                       if (ses->chans[i].server->tcpStatus != CifsExiting)
> > +                               ses->chans[i].server->tcpStatus = CifsNeedReconnect;
> >                         spin_unlock(&ses->chans[i].server->srv_lock);
> >                 }
> >                 spin_unlock(&ses->chan_lock);
> > @@ -2033,7 +2037,9 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
> >                                 kref_put(&ses->chans[i].iface->refcount, release_iface);
> >                                 ses->chans[i].iface = NULL;
> >                         }
> > -                       cifs_put_tcp_session(ses->chans[i].server, 0);
> > +
> > +                       if (ses->chans[i].server)
> > +                               cifs_put_tcp_session(ses->chans[i].server, 0);
> >                         ses->chans[i].server = NULL;
> >                 }
> >         }
> > diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
> > index c899b05c92f7..9d2228c2d7e5 100644
> > --- a/fs/smb/client/sess.c
> > +++ b/fs/smb/client/sess.c
> > @@ -69,7 +69,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
> >
> >  /* channel helper functions. assumed that chan_lock is held by caller. */
> >
> > -unsigned int
> > +int
> >  cifs_ses_get_chan_index(struct cifs_ses *ses,
> >                         struct TCP_Server_Info *server)
> >  {
> > @@ -85,14 +85,16 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
> >                 cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
> >                          server->conn_id);
> >         WARN_ON(1);
> > -       return 0;
> > +       return CIFS_INVAL_CHAN_INDEX;
> >  }
> >
> >  void
> >  cifs_chan_set_in_reconnect(struct cifs_ses *ses,
> >                              struct TCP_Server_Info *server)
> >  {
> > -       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return;
> >
> >         ses->chans[chan_index].in_reconnect = true;
> >  }
> > @@ -102,6 +104,8 @@ cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
> >                              struct TCP_Server_Info *server)
> >  {
> >         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return;
> >
> >         ses->chans[chan_index].in_reconnect = false;
> >  }
> > @@ -111,6 +115,8 @@ cifs_chan_in_reconnect(struct cifs_ses *ses,
> >                           struct TCP_Server_Info *server)
> >  {
> >         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return true;    /* err on the safer side */
> >
> >         return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
> >  }
> > @@ -120,6 +126,8 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses,
> >                              struct TCP_Server_Info *server)
> >  {
> >         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return;
> >
> >         set_bit(chan_index, &ses->chans_need_reconnect);
> >         cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
> > @@ -131,6 +139,8 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
> >                                struct TCP_Server_Info *server)
> >  {
> >         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return;
> >
> >         clear_bit(chan_index, &ses->chans_need_reconnect);
> >         cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
> > @@ -142,6 +152,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
> >                           struct TCP_Server_Info *server)
> >  {
> >         unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return true;    /* err on the safer side */
> >
> >         return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
> >  }
> > @@ -151,6 +163,8 @@ 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);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return true;    /* err on the safer side */
> >
> >         return ses->chans[chan_index].iface &&
> >                 ses->chans[chan_index].iface->is_active;
> > @@ -269,7 +283,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
> >
> >         spin_lock(&ses->chan_lock);
> >         chan_index = cifs_ses_get_chan_index(ses, server);
> > -       if (!chan_index) {
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
> >                 spin_unlock(&ses->chan_lock);
> >                 return 0;
> >         }
> > @@ -319,6 +333,9 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
> >
> >         spin_lock(&ses->chan_lock);
> >         chan_index = cifs_ses_get_chan_index(ses, server);
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX)
> > +               return 0;
> > +
> >         ses->chans[chan_index].iface = iface;
> >
> >         /* No iface is found. if secondary chan, drop connection */
> > diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
> > index 23c50ed7d4b5..84ea67301303 100644
> > --- a/fs/smb/client/smb2transport.c
> > +++ b/fs/smb/client/smb2transport.c
> > @@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
> >                       ses->ses_status == SES_GOOD);
> >
> >         chan_index = cifs_ses_get_chan_index(ses, server);
> > -       /* TODO: introduce ref counting for channels when the can be freed */
> > +       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
> > +               spin_unlock(&ses->chan_lock);
> > +               spin_unlock(&ses->ses_lock);
> > +
> > +               return -EINVAL;
> > +       }
> > +
> >         spin_unlock(&ses->chan_lock);
> >         spin_unlock(&ses->ses_lock);
> >
> > --
> > 2.34.1
> >
>
>
> --
> Thanks,
>
> Steve
diff mbox series

Patch

diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index a9dfecc397a8..9fca09539728 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -136,6 +136,11 @@  cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
 {
 	struct TCP_Server_Info *server = chan->server;
 
+	if (!server) {
+		seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
+		return;
+	}
+
 	seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
 		   "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
 		   "\n\t\tTCP status: %d Instance: %d"
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 02082621d8e0..552ed441281a 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1050,6 +1050,7 @@  struct cifs_ses {
 	spinlock_t chan_lock;
 	/* ========= begin: protected by chan_lock ======== */
 #define CIFS_MAX_CHANNELS 16
+#define CIFS_INVAL_CHAN_INDEX (-1)
 #define CIFS_ALL_CHANNELS_SET(ses)	\
 	((1UL << (ses)->chan_count) - 1)
 #define CIFS_ALL_CHANS_GOOD(ses)		\
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 0c37eefa18a5..65c84b3d1a65 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -616,7 +616,7 @@  bool is_server_using_iface(struct TCP_Server_Info *server,
 bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
 void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
 
-unsigned int
+int
 cifs_ses_get_chan_index(struct cifs_ses *ses,
 			struct TCP_Server_Info *server);
 void
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 97c9a32cff36..8393977e21ee 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -173,8 +173,12 @@  cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
 	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
 		spin_lock(&ses->chan_lock);
 		for (i = 0; i < ses->chan_count; i++) {
+			if (!ses->chans[i].server)
+				continue;
+
 			spin_lock(&ses->chans[i].server->srv_lock);
-			ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+			if (ses->chans[i].server->tcpStatus != CifsExiting)
+				ses->chans[i].server->tcpStatus = CifsNeedReconnect;
 			spin_unlock(&ses->chans[i].server->srv_lock);
 		}
 		spin_unlock(&ses->chan_lock);
@@ -2033,7 +2037,9 @@  void __cifs_put_smb_ses(struct cifs_ses *ses)
 				kref_put(&ses->chans[i].iface->refcount, release_iface);
 				ses->chans[i].iface = NULL;
 			}
-			cifs_put_tcp_session(ses->chans[i].server, 0);
+
+			if (ses->chans[i].server)
+				cifs_put_tcp_session(ses->chans[i].server, 0);
 			ses->chans[i].server = NULL;
 		}
 	}
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index c899b05c92f7..9d2228c2d7e5 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -69,7 +69,7 @@  bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
 
 /* channel helper functions. assumed that chan_lock is held by caller. */
 
-unsigned int
+int
 cifs_ses_get_chan_index(struct cifs_ses *ses,
 			struct TCP_Server_Info *server)
 {
@@ -85,14 +85,16 @@  cifs_ses_get_chan_index(struct cifs_ses *ses,
 		cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
 			 server->conn_id);
 	WARN_ON(1);
-	return 0;
+	return CIFS_INVAL_CHAN_INDEX;
 }
 
 void
 cifs_chan_set_in_reconnect(struct cifs_ses *ses,
 			     struct TCP_Server_Info *server)
 {
-	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return;
 
 	ses->chans[chan_index].in_reconnect = true;
 }
@@ -102,6 +104,8 @@  cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
 			     struct TCP_Server_Info *server)
 {
 	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return;
 
 	ses->chans[chan_index].in_reconnect = false;
 }
@@ -111,6 +115,8 @@  cifs_chan_in_reconnect(struct cifs_ses *ses,
 			  struct TCP_Server_Info *server)
 {
 	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return true;	/* err on the safer side */
 
 	return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
 }
@@ -120,6 +126,8 @@  cifs_chan_set_need_reconnect(struct cifs_ses *ses,
 			     struct TCP_Server_Info *server)
 {
 	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return;
 
 	set_bit(chan_index, &ses->chans_need_reconnect);
 	cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
@@ -131,6 +139,8 @@  cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
 			       struct TCP_Server_Info *server)
 {
 	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return;
 
 	clear_bit(chan_index, &ses->chans_need_reconnect);
 	cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
@@ -142,6 +152,8 @@  cifs_chan_needs_reconnect(struct cifs_ses *ses,
 			  struct TCP_Server_Info *server)
 {
 	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return true;	/* err on the safer side */
 
 	return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
 }
@@ -151,6 +163,8 @@  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);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return true;	/* err on the safer side */
 
 	return ses->chans[chan_index].iface &&
 		ses->chans[chan_index].iface->is_active;
@@ -269,7 +283,7 @@  cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
 
 	spin_lock(&ses->chan_lock);
 	chan_index = cifs_ses_get_chan_index(ses, server);
-	if (!chan_index) {
+	if (chan_index == CIFS_INVAL_CHAN_INDEX) {
 		spin_unlock(&ses->chan_lock);
 		return 0;
 	}
@@ -319,6 +333,9 @@  cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
 
 	spin_lock(&ses->chan_lock);
 	chan_index = cifs_ses_get_chan_index(ses, server);
+	if (chan_index == CIFS_INVAL_CHAN_INDEX)
+		return 0;
+
 	ses->chans[chan_index].iface = iface;
 
 	/* No iface is found. if secondary chan, drop connection */
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 23c50ed7d4b5..84ea67301303 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -413,7 +413,13 @@  generate_smb3signingkey(struct cifs_ses *ses,
 		      ses->ses_status == SES_GOOD);
 
 	chan_index = cifs_ses_get_chan_index(ses, server);
-	/* TODO: introduce ref counting for channels when the can be freed */
+	if (chan_index == CIFS_INVAL_CHAN_INDEX) {
+		spin_unlock(&ses->chan_lock);
+		spin_unlock(&ses->ses_lock);
+
+		return -EINVAL;
+	}
+
 	spin_unlock(&ses->chan_lock);
 	spin_unlock(&ses->ses_lock);