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

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e10823c7 7ed10e16
...@@ -417,10 +417,13 @@ enum qeth_qdio_out_buffer_state { ...@@ -417,10 +417,13 @@ enum qeth_qdio_out_buffer_state {
QETH_QDIO_BUF_EMPTY, QETH_QDIO_BUF_EMPTY,
/* Filled by driver; owned by hardware in order to be sent. */ /* Filled by driver; owned by hardware in order to be sent. */
QETH_QDIO_BUF_PRIMED, QETH_QDIO_BUF_PRIMED,
/* Identified to be pending in TPQ. */ /* Discovered by the TX completion code: */
QETH_QDIO_BUF_PENDING, QETH_QDIO_BUF_PENDING,
/* Found in completion queue. */ /* Finished by the TX completion code: */
QETH_QDIO_BUF_IN_CQ, 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. */ /* Handled via transfer pending / completion queue. */
QETH_QDIO_BUF_HANDLED_DELAYED, QETH_QDIO_BUF_HANDLED_DELAYED,
}; };
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <net/iucv/af_iucv.h> #include <net/iucv/af_iucv.h>
#include <net/dsfield.h> #include <net/dsfield.h>
#include <net/sock.h>
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/chpid.h> #include <asm/chpid.h>
...@@ -499,17 +500,12 @@ static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx, ...@@ -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, static void qeth_qdio_handle_aob(struct qeth_card *card,
unsigned long phys_aob_addr) unsigned long phys_aob_addr)
{ {
enum qeth_qdio_out_buffer_state new_state = QETH_QDIO_BUF_QAOB_OK;
struct qaob *aob; struct qaob *aob;
struct qeth_qdio_out_buffer *buffer; struct qeth_qdio_out_buffer *buffer;
enum iucv_tx_notify notification; enum iucv_tx_notify notification;
...@@ -521,22 +517,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, ...@@ -521,22 +517,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
buffer = (struct qeth_qdio_out_buffer *) aob->user1; buffer = (struct qeth_qdio_out_buffer *) aob->user1;
QETH_CARD_TEXT_(card, 5, "%lx", 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 /* Free dangling allocations. The attached skbs are handled by
* qeth_cleanup_handled_pending(). * qeth_cleanup_handled_pending().
*/ */
...@@ -548,7 +528,33 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, ...@@ -548,7 +528,33 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
if (data && buffer->is_header[i]) if (data && buffer->is_header[i])
kmem_cache_free(qeth_core_header_cache, data); 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); atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
break;
default:
WARN_ON_ONCE(1);
}
qdio_release_aob(aob); qdio_release_aob(aob);
} }
...@@ -1405,7 +1411,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q, ...@@ -1405,7 +1411,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
skb_queue_walk(&buf->skb_list, skb) { skb_queue_walk(&buf->skb_list, skb) {
QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); 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); 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, ...@@ -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 qeth_qdio_out_q *queue = buf->q;
struct sk_buff *skb; 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) if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING)
qeth_notify_skbs(queue, buf, TX_NOTIFY_GENERALERROR); qeth_notify_skbs(queue, buf, TX_NOTIFY_GENERALERROR);
...@@ -5869,9 +5872,32 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, ...@@ -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, if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED,
QETH_QDIO_BUF_PENDING) == QETH_QDIO_BUF_PENDING) ==
QETH_QDIO_BUF_PRIMED) QETH_QDIO_BUF_PRIMED) {
qeth_notify_skbs(queue, buffer, TX_NOTIFY_PENDING); 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); QETH_CARD_TEXT_(card, 5, "pel%u", bidx);
/* prepare the queue slot for re-use: */ /* prepare the queue slot for re-use: */
......
...@@ -985,32 +985,19 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) ...@@ -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 * change notification' and thus can support the learning_sync bridgeport
* attribute * attribute
* @card: qeth_card structure pointer * @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) static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
{ {
struct qeth_priv *priv = netdev_priv(card->dev); struct qeth_priv *priv = netdev_priv(card->dev);
bool dev2br_supported; bool dev2br_supported;
int rc;
QETH_CARD_TEXT(card, 2, "d2brsup"); QETH_CARD_TEXT(card, 2, "d2brsup");
if (!IS_IQD(card)) if (!IS_IQD(card))
return; return;
/* dev2br requires valid cssid,iid,chid */ /* dev2br requires valid cssid,iid,chid */
if (!card->info.ids_valid) { dev2br_supported = card->info.ids_valid &&
dev2br_supported = false; css_general_characteristics.enarf;
} 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;
}
QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported); QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);
if (dev2br_supported) if (dev2br_supported)
...@@ -2233,7 +2220,6 @@ static int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok) ...@@ -2233,7 +2220,6 @@ static int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok)
struct net_device *dev = card->dev; struct net_device *dev = card->dev;
int rc = 0; int rc = 0;
/* query before bridgeport_notification may be enabled */
qeth_l2_detect_dev2br_support(card); qeth_l2_detect_dev2br_support(card);
mutex_lock(&card->sbp_lock); mutex_lock(&card->sbp_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment