Commit 207d0bfc authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 's390-qeth-fixes-2020-11-20'

Julian Wiedmann says:

====================
s390/qeth: fixes 2020-11-20

This brings several fixes for qeth's af_iucv-specific code paths.

Also one fix by Alexandra for the recently added BR_LEARNING_SYNC
support. We want to trust the feature indication bit, so that HW can
mask it out if there's any issues on their end.
====================

Link: https://lore.kernel.org/r/20201120090939.101406-1-jwi@linux.ibm.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e10823c7 7ed10e16
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -417,10 +417,13 @@ enum qeth_qdio_out_buffer_state {
	QETH_QDIO_BUF_EMPTY,
	/* Filled by driver; owned by hardware in order to be sent. */
	QETH_QDIO_BUF_PRIMED,
	/* Identified to be pending in TPQ. */
	/* Discovered by the TX completion code: */
	QETH_QDIO_BUF_PENDING,
	/* Found in completion queue. */
	QETH_QDIO_BUF_IN_CQ,
	/* Finished by the TX completion code: */
	QETH_QDIO_BUF_NEED_QAOB,
	/* Received QAOB notification on CQ: */
	QETH_QDIO_BUF_QAOB_OK,
	QETH_QDIO_BUF_QAOB_ERROR,
	/* Handled via transfer pending / completion queue. */
	QETH_QDIO_BUF_HANDLED_DELAYED,
};
+54 −28
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@

#include <net/iucv/af_iucv.h>
#include <net/dsfield.h>
#include <net/sock.h>

#include <asm/ebcdic.h>
#include <asm/chpid.h>
@@ -499,17 +500,12 @@ static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx,

		}
	}
	if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) ==
					QETH_QDIO_BUF_HANDLED_DELAYED)) {
		/* for recovery situations */
		qeth_init_qdio_out_buf(q, bidx);
		QETH_CARD_TEXT(q->card, 2, "clprecov");
	}
}

static void qeth_qdio_handle_aob(struct qeth_card *card,
				 unsigned long phys_aob_addr)
{
	enum qeth_qdio_out_buffer_state new_state = QETH_QDIO_BUF_QAOB_OK;
	struct qaob *aob;
	struct qeth_qdio_out_buffer *buffer;
	enum iucv_tx_notify notification;
@@ -521,22 +517,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
	buffer = (struct qeth_qdio_out_buffer *) aob->user1;
	QETH_CARD_TEXT_(card, 5, "%lx", aob->user1);

	if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED,
			   QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) {
		notification = TX_NOTIFY_OK;
	} else {
		WARN_ON_ONCE(atomic_read(&buffer->state) !=
							QETH_QDIO_BUF_PENDING);
		atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ);
		notification = TX_NOTIFY_DELAYED_OK;
	}

	if (aob->aorc != 0)  {
		QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc);
		notification = qeth_compute_cq_notification(aob->aorc, 1);
	}
	qeth_notify_skbs(buffer->q, buffer, notification);

	/* Free dangling allocations. The attached skbs are handled by
	 * qeth_cleanup_handled_pending().
	 */
@@ -548,7 +528,33 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
		if (data && buffer->is_header[i])
			kmem_cache_free(qeth_core_header_cache, data);
	}

	if (aob->aorc) {
		QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc);
		new_state = QETH_QDIO_BUF_QAOB_ERROR;
	}

	switch (atomic_xchg(&buffer->state, new_state)) {
	case QETH_QDIO_BUF_PRIMED:
		/* Faster than TX completion code. */
		notification = qeth_compute_cq_notification(aob->aorc, 0);
		qeth_notify_skbs(buffer->q, buffer, notification);
		atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
		break;
	case QETH_QDIO_BUF_PENDING:
		/* TX completion code is active and will handle the async
		 * completion for us.
		 */
		break;
	case QETH_QDIO_BUF_NEED_QAOB:
		/* TX completion code is already finished. */
		notification = qeth_compute_cq_notification(aob->aorc, 1);
		qeth_notify_skbs(buffer->q, buffer, notification);
		atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
		break;
	default:
		WARN_ON_ONCE(1);
	}

	qdio_release_aob(aob);
}
@@ -1405,7 +1411,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
	skb_queue_walk(&buf->skb_list, skb) {
		QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
		QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb);
		if (skb->protocol == htons(ETH_P_AF_IUCV) && skb->sk)
		if (skb->sk && skb->sk->sk_family == PF_IUCV)
			iucv_sk(skb->sk)->sk_txnotify(skb, notification);
	}
}
@@ -1416,9 +1422,6 @@ static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error,
	struct qeth_qdio_out_q *queue = buf->q;
	struct sk_buff *skb;

	/* release may never happen from within CQ tasklet scope */
	WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ);

	if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING)
		qeth_notify_skbs(queue, buf, TX_NOTIFY_GENERALERROR);

@@ -5869,9 +5872,32 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,

		if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED,
						   QETH_QDIO_BUF_PENDING) ==
		    QETH_QDIO_BUF_PRIMED)
		    QETH_QDIO_BUF_PRIMED) {
			qeth_notify_skbs(queue, buffer, TX_NOTIFY_PENDING);

			/* Handle race with qeth_qdio_handle_aob(): */
			switch (atomic_xchg(&buffer->state,
					    QETH_QDIO_BUF_NEED_QAOB)) {
			case QETH_QDIO_BUF_PENDING:
				/* No concurrent QAOB notification. */
				break;
			case QETH_QDIO_BUF_QAOB_OK:
				qeth_notify_skbs(queue, buffer,
						 TX_NOTIFY_DELAYED_OK);
				atomic_set(&buffer->state,
					   QETH_QDIO_BUF_HANDLED_DELAYED);
				break;
			case QETH_QDIO_BUF_QAOB_ERROR:
				qeth_notify_skbs(queue, buffer,
						 TX_NOTIFY_DELAYED_GENERALERROR);
				atomic_set(&buffer->state,
					   QETH_QDIO_BUF_HANDLED_DELAYED);
				break;
			default:
				WARN_ON_ONCE(1);
			}
		}

		QETH_CARD_TEXT_(card, 5, "pel%u", bidx);

		/* prepare the queue slot for re-use: */
+2 −16
Original line number Diff line number Diff line
@@ -985,32 +985,19 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
 *	change notification' and thus can support the learning_sync bridgeport
 *	attribute
 *	@card: qeth_card structure pointer
 *
 *	This is a destructive test and must be called before dev2br or
 *	bridgeport address notification is enabled!
 */
static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
{
	struct qeth_priv *priv = netdev_priv(card->dev);
	bool dev2br_supported;
	int rc;

	QETH_CARD_TEXT(card, 2, "d2brsup");
	if (!IS_IQD(card))
		return;

	/* dev2br requires valid cssid,iid,chid */
	if (!card->info.ids_valid) {
		dev2br_supported = false;
	} else if (css_general_characteristics.enarf) {
		dev2br_supported = true;
	} else {
		/* Old machines don't have the feature bit:
		 * Probe by testing whether a disable succeeds
		 */
		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
		dev2br_supported = !rc;
	}
	dev2br_supported = card->info.ids_valid &&
			   css_general_characteristics.enarf;
	QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);

	if (dev2br_supported)
@@ -2233,7 +2220,6 @@ static int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok)
	struct net_device *dev = card->dev;
	int rc = 0;

	/* query before bridgeport_notification may be enabled */
	qeth_l2_detect_dev2br_support(card);

	mutex_lock(&card->sbp_lock);