Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 072f7940 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by Jakub Kicinski
Browse files

s390/qeth: serialize cmd reply with concurrent timeout



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: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
parent a1794de8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -629,6 +629,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;
+20 −0
Original line number Diff line number Diff line
@@ -544,6 +544,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;
}
@@ -799,6 +800,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);
@@ -807,7 +815,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);
@@ -1749,6 +1759,16 @@ static int qeth_send_control_data(struct qeth_card *card,
		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);