lkml.org 
[lkml]   [2019]   [Aug]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL 5.2 21/76] s390/qeth: serialize cmd reply with concurrent timeout
    Date
    From: Julian Wiedmann <jwi@linux.ibm.com>

    [ Upstream commit 072f79400032f74917726cf76f4248367ea2b5b8 ]

    Callbacks for a cmd reply run outside the protection of card->lock, to
    allow for additional cmds to be issued & enqueued in parallel.

    When qeth_send_control_data() bails out for a cmd without having
    received a reply (eg. due to timeout), its callback may concurrently be
    processing a reply that just arrived. In this case, the callback
    potentially accesses a stale reply->reply_param area that eg. was
    on-stack and has already been released.

    To avoid this race, add some locking so that qeth_send_control_data()
    can (1) wait for a concurrently running callback, and (2) zap any
    pending callback that still wants to run.

    Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
    Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/s390/net/qeth_core.h | 1 +
    drivers/s390/net/qeth_core_main.c | 20 ++++++++++++++++++++
    2 files changed, 21 insertions(+)

    diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
    index 784a2e76a1b04..c5f60f95e8db9 100644
    --- a/drivers/s390/net/qeth_core.h
    +++ b/drivers/s390/net/qeth_core.h
    @@ -640,6 +640,7 @@ struct qeth_seqno {
    struct qeth_reply {
    struct list_head list;
    struct completion received;
    + spinlock_t lock;
    int (*callback)(struct qeth_card *, struct qeth_reply *,
    unsigned long);
    u32 seqno;
    diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
    index b1823d75dd35c..6b8f99e7d8a81 100644
    --- a/drivers/s390/net/qeth_core_main.c
    +++ b/drivers/s390/net/qeth_core_main.c
    @@ -548,6 +548,7 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
    if (reply) {
    refcount_set(&reply->refcnt, 1);
    init_completion(&reply->received);
    + spin_lock_init(&reply->lock);
    }
    return reply;
    }
    @@ -832,6 +833,13 @@ static void qeth_issue_next_read_cb(struct qeth_card *card,

    if (!reply->callback) {
    rc = 0;
    + goto no_callback;
    + }
    +
    + spin_lock_irqsave(&reply->lock, flags);
    + if (reply->rc) {
    + /* Bail out when the requestor has already left: */
    + rc = reply->rc;
    } else {
    if (cmd) {
    reply->offset = (u16)((char *)cmd - (char *)iob->data);
    @@ -840,7 +848,9 @@ static void qeth_issue_next_read_cb(struct qeth_card *card,
    rc = reply->callback(card, reply, (unsigned long)iob);
    }
    }
    + spin_unlock_irqrestore(&reply->lock, flags);

    +no_callback:
    if (rc <= 0)
    qeth_notify_reply(reply, rc);
    qeth_put_reply(reply);
    @@ -1880,6 +1890,16 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
    rc = (timeout == -ERESTARTSYS) ? -EINTR : -ETIME;

    qeth_dequeue_reply(card, reply);
    +
    + if (reply_cb) {
    + /* Wait until the callback for a late reply has completed: */
    + spin_lock_irq(&reply->lock);
    + if (rc)
    + /* Zap any callback that's still pending: */
    + reply->rc = rc;
    + spin_unlock_irq(&reply->lock);
    + }
    +
    if (!rc)
    rc = reply->rc;
    qeth_put_reply(reply);
    --
    2.20.1
    \
     
     \ /
      Last update: 2019-08-29 20:33    [W:4.103 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site