Re: [PATCH 08/13] cifs: handle cancelled requests better

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

 



On Tue, 14 Dec 2010 15:33:48 -0600
Steve French <smfrench@xxxxxxxxx> wrote:

> Why wouldn't we issue SMB NTCancel on these?  That way we only have to
> wait until the timeout for the NTCancel (at worst) and can't leak midq
> entries.
> 

I suppose we could, but...

a) windows doesn't do it

b) we don't have appropriate infrastructure for it

...sounds like a good follow-on project, but not something I really
want to tackle in this set.

> On Tue, Dec 14, 2010 at 2:40 PM, Pavel Shilovsky <piastry@xxxxxxxxxxx> wrote:
> > 2010/12/14 Jeff Layton <jlayton@xxxxxxxxxx>:
> >> On Tue, 14 Dec 2010 10:24:28 +0300
> >> Pavel Shilovsky <piastry@xxxxxxxxxxx> wrote:
> >>
> >>> 2010/12/10 Jeff Layton <jlayton@xxxxxxxxxx>:
> >>> > Currently, when a request is cancelled via signal, we delete the mid
> >>> > immediately. If the request was already transmitted however, the client
> >>> > is still likely to receive a response. When it does, it won't recognize
> >>> > it however and will pop a printk.
> >>> >
> >>> > It's also a little dangerous to just delete the mid entry like this. We
> >>> > may end up reusing that mid. If we do then we could potentially get the
> >>> > response from the first request confused with the later one.
> >>> >
> >>> > Prevent the reuse of mids by marking them as cancelled and keeping them
> >>> > on the pending_mid_q list. If the reply comes in, we'll delete it from
> >>> > the list then. If it never comes, then we'll delete it at reconnect
> >>> > or when cifsd comes down.
> >>> >
> >>> > Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
> >>> > ---
> >>> >  fs/cifs/cifsglob.h  |    2 +-
> >>> >  fs/cifs/cifsproto.h |    1 +
> >>> >  fs/cifs/connect.c   |   44 +++++++++++++++++++++++++++++++++++++-------
> >>> >  fs/cifs/transport.c |   30 ++++++++++++++++++++++--------
> >>> >  4 files changed, 61 insertions(+), 16 deletions(-)
> >>> >
> >>> > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> >>> > index cc43ada..fb75f04 100644
> >>> > --- a/fs/cifs/cifsglob.h
> >>> > +++ b/fs/cifs/cifsglob.h
> >>> > @@ -621,7 +621,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
> >>> >  #define   MID_REQUEST_SUBMITTED 2
> >>> >  #define   MID_RESPONSE_RECEIVED 4
> >>> >  #define   MID_RETRY_NEEDED      8 /* session closed while this request out */
> >>> > -#define   MID_NO_RESP_NEEDED 0x10
> >>> > +#define   MID_REQUEST_CANCELLED 0x10 /* discard any reply */
> >>> >
> >>> >  /* Types of response buffer returned from SendReceive2 */
> >>> >  #define   CIFS_NO_BUFFER        0    /* Response buffer not returned */
> >>> > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> >>> > index fe77e69..a8fc606 100644
> >>> > --- a/fs/cifs/cifsproto.h
> >>> > +++ b/fs/cifs/cifsproto.h
> >>> > @@ -61,6 +61,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
> >>> >                const char *fullpath, const struct dfs_info3_param *ref,
> >>> >                char **devname);
> >>> >  /* extern void renew_parental_timestamps(struct dentry *direntry);*/
> >>> > +extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
> >>> >  extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
> >>> >                        struct smb_hdr * /* input */ ,
> >>> >                        struct smb_hdr * /* out */ ,
> >>> > diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> >>> > index 7e20ece..0feb592 100644
> >>> > --- a/fs/cifs/connect.c
> >>> > +++ b/fs/cifs/connect.c
> >>> > @@ -133,7 +133,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> >>> >  {
> >>> >        int rc = 0;
> >>> >        struct list_head *tmp, *tmp2;
> >>> > -       struct list_head retry;
> >>> > +       struct list_head retry, dispose;
> >>> >        struct cifsSesInfo *ses;
> >>> >        struct cifsTconInfo *tcon;
> >>> >        struct mid_q_entry *mid_entry;
> >>> > @@ -192,14 +192,23 @@ cifs_reconnect(struct TCP_Server_Info *server)
> >>> >         */
> >>> >        cFYI(1, "%s: moving mids to retry list", __func__);
> >>> >        INIT_LIST_HEAD(&retry);
> >>> > +       INIT_LIST_HEAD(&dispose);
> >>> >        spin_lock(&GlobalMid_Lock);
> >>> >        list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
> >>> >                mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
> >>> >                if (mid_entry->midState == MID_REQUEST_SUBMITTED)
> >>> >                        list_move(tmp, &retry);
> >>> > +               else if (mid_entry->midState == MID_REQUEST_CANCELLED)
> >>> > +                       list_move(tmp, &dispose);
> >>> >        }
> >>> >        spin_unlock(&GlobalMid_Lock);
> >>> >
> >>> > +       /* now walk private dispose list and delete entries */
> >>> > +       list_for_each_safe(tmp, tmp2, &dispose) {
> >>> > +               mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
> >>> > +               DeleteMidQEntry(mid_entry);
> >>> > +       }
> >>> > +
> >>> >        while ((server->tcpStatus != CifsExiting) &&
> >>> >               (server->tcpStatus != CifsGood)) {
> >>> >                try_to_freeze();
> >>> > @@ -219,7 +228,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> >>> >                }
> >>> >        }
> >>> >
> >>> > -       /* now, issue callback for all mids in flight */
> >>> > +       /* issue callback for all mids in flight */
> >>> >        list_for_each_safe(tmp, tmp2, &retry) {
> >>> >                list_del_init(tmp);
> >>> >                mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
> >>> > @@ -575,9 +584,13 @@ incomplete_rcv:
> >>> >                list_for_each(tmp, &server->pending_mid_q) {
> >>> >                        mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
> >>> >
> >>> > -                       if ((mid_entry->mid == smb_buffer->Mid) &&
> >>> > -                           (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
> >>> > -                           (mid_entry->command == smb_buffer->Command)) {
> >>> > +                       if (mid_entry->mid != smb_buffer->Mid)
> >>> > +                               goto next_mid;
> >>> > +                       if (mid_entry->command != smb_buffer->Command)
> >>> > +                               goto next_mid;
> >>> > +                       if (mid_entry->midState == MID_REQUEST_CANCELLED)
> >>> > +                               break;
> >>> > +                       if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
> >>> >                                if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
> >>> >                                        /* We have a multipart transact2 resp */
> >>> >                                        isMultiRsp = true;
> >>> > @@ -623,11 +636,16 @@ multi_t2_fnd:
> >>> >                                server->lstrp = jiffies;
> >>> >                                break;
> >>> >                        }
> >>> > +next_mid:
> >>> >                        mid_entry = NULL;
> >>> >                }
> >>> >                spin_unlock(&GlobalMid_Lock);
> >>> >
> >>> >                if (mid_entry != NULL) {
> >>> > +                       if (mid_entry->midState == MID_REQUEST_CANCELLED) {
> >>> > +                               DeleteMidQEntry(mid_entry);
> >>> > +                               continue;
> >>> > +                       }
> >>> >                        mid_entry->callback(mid_entry);
> >>> >                        /* Was previous buf put in mpx struct for multi-rsp? */
> >>> >                        if (!isMultiRsp) {
> >>> > @@ -704,6 +722,9 @@ multi_t2_fnd:
> >>> >                }
> >>> >                spin_unlock(&cifs_tcp_ses_lock);
> >>> >        } else {
> >>> > +               struct mid_q_entry *tmp_mid;
> >>> > +               struct list_head dispose;
> >>> > +
> >>> >                /* although we can not zero the server struct pointer yet,
> >>> >                since there are active requests which may depnd on them,
> >>> >                mark the corresponding SMB sessions as exiting too */
> >>> > @@ -713,17 +734,26 @@ multi_t2_fnd:
> >>> >                        ses->status = CifsExiting;
> >>> >                }
> >>> >
> >>> > +               INIT_LIST_HEAD(&dispose);
> >>> >                spin_lock(&GlobalMid_Lock);
> >>> > -               list_for_each(tmp, &server->pending_mid_q) {
> >>> > -               mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
> >>> > +               list_for_each_entry_safe(mid_entry, tmp_mid,
> >>> > +                                        &server->pending_mid_q, qhead) {
> >>> >                        if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
> >>> >                                cFYI(1, "Clearing Mid 0x%x - issuing callback",
> >>> >                                         mid_entry->mid);
> >>> >                                mid_entry->callback(mid_entry);
> >>> > +                       } else if (mid_entry->midState == MID_REQUEST_CANCELLED) {
> >>> > +                               cFYI(1, "Clearing Mid 0x%x - Cancelled",
> >>> > +                                       mid_entry->mid);
> >>> > +                               list_move(&mid_entry->qhead, &dispose);
> >>>
> >>> Why do we need another list here? It seems to me that we can simply
> >>> delete cancelled mid.
> >>>
> >>
> >> Nope, we can't. DeleteMidQEntry will try to take the GlobalMid_Lock on
> >> its own, so you can't call it there without deadlocking. You also can't
> >> drop the spinlock and then call DeleteMidQEntry since that might mean
> >> that the list will change in the middle of walking it. We could add a
> >> "DeleteMidQEntry_locked", but dealing with locked and non-locked
> >> variants gets messy.
> >>
> >> Moving it to a private dispose list and then walking that list outside
> >> of the lock takes care of those problems. I added the "retry" list in
> >> this function for similar reasons.
> >>
> >>> >                        }
> >>> >                }
> >>> >                spin_unlock(&GlobalMid_Lock);
> >>> >                spin_unlock(&cifs_tcp_ses_lock);
> >>> > +
> >>> > +               /* now delete all of the cancelled mids */
> >>> > +               list_for_each_entry_safe(mid_entry, tmp_mid, &dispose, qhead)
> >>> > +                       DeleteMidQEntry(mid_entry);
> >>> >                /* 1/8th of sec is more than enough time for them to exit */
> >>> >                msleep(125);
> >>> >        }
> >>> > diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
> >>> > index 79647db..97a1170 100644
> >>> > --- a/fs/cifs/transport.c
> >>> > +++ b/fs/cifs/transport.c
> >>> > @@ -81,7 +81,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
> >>> >        return temp;
> >>> >  }
> >>> >
> >>> > -static void
> >>> > +void
> >>> >  DeleteMidQEntry(struct mid_q_entry *midEntry)
> >>> >  {
> >>> >  #ifdef CONFIG_CIFS_STATS2
> >>> > @@ -480,8 +480,13 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
> >>> >                goto out;
> >>> >
> >>> >        rc = wait_for_response(ses->server, midQ);
> >>> > -       if (rc != 0)
> >>> > -               goto out;
> >>> > +       if (rc != 0) {
> >>> > +               /* no longer considered to be "in-flight" */
> >>> > +               midQ->midState = MID_REQUEST_CANCELLED;
> >>> > +               atomic_dec(&ses->server->inFlight);
> >>> > +               wake_up(&ses->server->request_q);
> >>> > +               return rc;
> >>> > +       }
> >>> >
> >>> >        rc = handle_mid_result(midQ, ses->server);
> >>> >        if (rc != 0)
> >>> > @@ -623,8 +628,13 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
> >>> >                goto out;
> >>> >
> >>> >        rc = wait_for_response(ses->server, midQ);
> >>> > -       if (rc != 0)
> >>> > -               goto out;
> >>> > +       if (rc != 0) {
> >>> > +               /* no longer considered to be "in-flight" */
> >>> > +               midQ->midState = MID_REQUEST_CANCELLED;
> >>> > +               atomic_dec(&ses->server->inFlight);
> >>> > +               wake_up(&ses->server->request_q);
> >>> > +               return rc;
> >>> > +       }
> >>> >
> >>> >        rc = handle_mid_result(midQ, ses->server);
> >>> >        if (rc != 0)
> >>> > @@ -842,10 +852,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
> >>> >                        }
> >>> >                }
> >>> >
> >>> > -               if (wait_for_response(ses->server, midQ) == 0) {
> >>> > -                       /* We got the response - restart system call. */
> >>> > -                       rstart = 1;
> >>> > +               rc = wait_for_response(ses->server, midQ);
> >>> > +               if (rc) {
> >>> > +                       midQ->midState = MID_REQUEST_CANCELLED;
> >>> > +                       return rc;
> >>> >                }
> >>> > +
> >>> > +               /* We got the response - restart system call. */
> >>> > +               rstart = 1;
> >>> >        }
> >>> >
> >>> >        rc = handle_mid_result(midQ, ses->server);
> >>> > --
> >>> > 1.7.3.2
> >>> >
> >>> > --
> >>> > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> >>> > the body of a message to majordomo@xxxxxxxxxxxxxxx
> >>> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >>> >
> >>>
> >>>
> >>>
> >>
> >>
> >> --
> >> Jeff Layton <jlayton@xxxxxxxxxx>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> >> the body of a message to majordomo@xxxxxxxxxxxxxxx
> >> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >>
> >
> > Ok. Adding DeleteMidQEntry without lock/unlock looks better to me, but
> > anyway it's ok.
> >
> > Reviewed-by: Pavel Shilovsky <piastryyy@xxxxxxxxx>
> >
> > --
> > Best regards,
> > Pavel Shilovsky.
> >
> 
> 
> 


-- 
Jeff Layton <jlayton@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux