Re: [PATCH] cifs: try to pick channel with a minimum of credits

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Spent some time in this code path. Seems like this is more complicated
than I initially thought.
@Aurélien Aptel A few things to consider:
1. What if the credits that will be needed by the request is more than
3 (or any constant). We may still end up returning -EDEADLK to the
user when we don't find enough credits, and there are not enough
in_flight to satisfy the request. Ideally, we should still try other
channels.
2. Echo and oplock use 1 reserved credit each, which the regular
operations can steal, in case of shortage.
3. Reading server->credits without a lock can result in wildly
different values, since some CPU architectures may not update it
atomically. At worse, we may select a channel which may not have
enough credits when we get to it.

What if we do this...
wait_for_compound_request() can return -EDEADLK when it doesn't get
enough credits, and there are no requests in_flight.
In compound_send_recv(), after we call wait_for_compound_request(), we
can check it's return value. If it's -EDEADLK, we keep calling
cifs_pick_another_channel(ses, bitmask) (bitmask has bits set for
channels already selected and rejected) and
wait_for_compound_request() in a loop till we find a channel which has
enough credits, or we run out of channels; in which case we can return
-EDEADLK.
Makes sense? Do you see a problem with that approach?

Regards,
Shyam

On Fri, Mar 5, 2021 at 9:40 PM Steve French <smfrench@xxxxxxxxx> wrote:
>
> The comment caught my attention - is that accurate?  When a channel
> has fewer than 3 credits (assuming we had two reserved for oplock and
> echo), that doesn't mean that there are fewer than 3 credits in flight
> - just that current credits available is lower right?  So the channel
> could still recover as long as current credits + credits in flight is
> at least 3.
>
> +/*
> + * Min number of credits for a channel to be picked.
> + *
> + * Note that once a channel reaches this threshold it will never be
> + * picked again as no credits can be requested from it.
> + */
> +#define CIFS_CHANNEL_MIN_CREDITS 3
>
> On Fri, Mar 5, 2021 at 8:24 AM Aurélien Aptel <aaptel@xxxxxxxx> wrote:
> >
> > From: Aurelien Aptel <aaptel@xxxxxxxx>
> >
> > Check channel credits to prevent the client from using a starved
> > channel that cannot send anything.
> >
> > Special care must be taken in selecting the minimum value: when
> > channels are created they start off with a small amount that slowly
> > ramps up as the channel gets used. Thus a new channel might never be
> > picked if the min value is too small.
> >
> > Signed-off-by: Aurelien Aptel <aaptel@xxxxxxxx>
> > ---
> >  fs/cifs/transport.c | 57 ++++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 49 insertions(+), 8 deletions(-)
> >
> > diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
> > index e90a1d1380b0..7bb1584b3724 100644
> > --- a/fs/cifs/transport.c
> > +++ b/fs/cifs/transport.c
> > @@ -44,6 +44,14 @@
> >  /* Max number of iovectors we can use off the stack when sending requests. */
> >  #define CIFS_MAX_IOV_SIZE 8
> >
> > +/*
> > + * Min number of credits for a channel to be picked.
> > + *
> > + * Note that once a channel reaches this threshold it will never be
> > + * picked again as no credits can be requested from it.
> > + */
> > +#define CIFS_CHANNEL_MIN_CREDITS 3
> > +
> >  void
> >  cifs_wake_up_task(struct mid_q_entry *mid)
> >  {
> > @@ -1051,20 +1059,53 @@ cifs_cancelled_callback(struct mid_q_entry *mid)
> >  struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
> >  {
> >         uint index = 0;
> > +       struct TCP_Server_Info *s;
> >
> >         if (!ses)
> >                 return NULL;
> >
> > -       if (!ses->binding) {
> > -               /* round robin */
> > -               if (ses->chan_count > 1) {
> > -                       index = (uint)atomic_inc_return(&ses->chan_seq);
> > -                       index %= ses->chan_count;
> > -               }
> > -               return ses->chans[index].server;
> > -       } else {
> > +       if (ses->binding)
> >                 return cifs_ses_server(ses);
> > +
> > +       /*
> > +        * Channels are created right after the session is made. The
> > +        * count cannot change after that so it is not racy to check.
> > +        */
> > +       if (ses->chan_count == 1)
> > +               return ses->chans[index].server;
> > +
> > +       /* round robin */
> > +       index = (uint)atomic_inc_return(&ses->chan_seq);
> > +       index %= ses->chan_count;
> > +       s = ses->chans[index].server;
> > +
> > +       /*
> > +        * Checking server credits is racy, but getting a slightly
> > +        * stale value should not be an issue here
> > +        */
> > +       if (s->credits <= CIFS_CHANNEL_MIN_CREDITS) {
> > +               uint i;
> > +
> > +               cifs_dbg(VFS, "cannot pick conn_id=0x%llx not enough credits (%u)",
> > +                        s->conn_id,
> > +                        s->credits);
> > +
> > +               /*
> > +                * Look at all other channels starting from the next
> > +                * one and pick first possible channel.
> > +                */
> > +               for (i = 1; i < ses->chan_count; i++) {
> > +                       s = ses->chans[(index+i) % ses->chan_count].server;
> > +                       if (s->credits > CIFS_CHANNEL_MIN_CREDITS)
> > +                               return s;
> > +               }
> >         }
> > +
> > +       /*
> > +        * If none are possible, keep the initially picked one, but
> > +        * later on it will block to wait for credits or fail.
> > +        */
> > +       return ses->chans[index].server;
> >  }
> >
> >  int
> > --
> > 2.30.0
> >
>
>
> --
> Thanks,
>
> Steve



-- 
Regards,
Shyam




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux