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

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

 



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.
--
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