From b7f143d093e10cd39ae4a22d2f57ac853017f49e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:14 +0100 Subject: s390/qdio: implement IQD Multi-Write This allows IQD drivers to send out multiple SBALs with a single SIGA instruction. Signed-off-by: Julian Wiedmann Reviewed-by: Alexandra Winter Acked-by: Heiko Carstens Signed-off-by: David S. Miller --- drivers/s390/cio/qdio.h | 1 + drivers/s390/cio/qdio_main.c | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index a58b45df95d7..2a34a2ab9bf7 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -82,6 +82,7 @@ enum qdio_irq_states { #define QDIO_SIGA_WRITE 0x00 #define QDIO_SIGA_READ 0x01 #define QDIO_SIGA_SYNC 0x02 +#define QDIO_SIGA_WRITEM 0x03 #define QDIO_SIGA_WRITEQ 0x04 #define QDIO_SIGA_QEBSM_FLAG 0x80 diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5b63c505a2f7..7368407030b0 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -310,18 +310,19 @@ static inline int qdio_siga_sync_q(struct qdio_q *q) return qdio_siga_sync(q, q->mask, 0); } -static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, - unsigned long aob) +static int qdio_siga_output(struct qdio_q *q, unsigned int count, + unsigned int *busy_bit, unsigned long aob) { unsigned long schid = *((u32 *) &q->irq_ptr->schid); unsigned int fc = QDIO_SIGA_WRITE; u64 start_time = 0; int retries = 0, cc; - unsigned long laob = 0; - if (aob) { - fc = QDIO_SIGA_WRITEQ; - laob = aob; + if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q)) { + if (count > 1) + fc = QDIO_SIGA_WRITEM; + else if (aob) + fc = QDIO_SIGA_WRITEQ; } if (is_qebsm(q)) { @@ -329,7 +330,7 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, fc |= QDIO_SIGA_QEBSM_FLAG; } again: - cc = do_siga_output(schid, q->mask, busy_bit, fc, laob); + cc = do_siga_output(schid, q->mask, busy_bit, fc, aob); /* hipersocket busy condition */ if (unlikely(*busy_bit)) { @@ -781,7 +782,8 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start) return count; } -static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob) +static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count, + unsigned long aob) { int retries = 0, cc; unsigned int busy_bit; @@ -793,7 +795,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob) retry: qperf_inc(q, siga_write); - cc = qdio_siga_output(q, &busy_bit, aob); + cc = qdio_siga_output(q, count, &busy_bit, aob); switch (cc) { case 0: break; @@ -1526,7 +1528,7 @@ set: * @count: how many buffers are filled */ static int handle_outbound(struct qdio_q *q, unsigned int callflags, - int bufnr, int count) + unsigned int bufnr, unsigned int count) { const unsigned int scan_threshold = q->irq_ptr->scan_threshold; unsigned char state = 0; @@ -1549,13 +1551,10 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, if (queue_type(q) == QDIO_IQDIO_QFMT) { unsigned long phys_aob = 0; - /* One SIGA-W per buffer required for unicast HSI */ - WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); - - if (q->u.out.use_cq) + if (q->u.out.use_cq && count == 1) phys_aob = qdio_aob_for_buffer(&q->u.out, bufnr); - rc = qdio_kick_outbound_q(q, phys_aob); + rc = qdio_kick_outbound_q(q, count, phys_aob); } else if (need_siga_sync(q)) { rc = qdio_siga_sync_q(q); } else if (count < QDIO_MAX_BUFFERS_PER_Q && @@ -1564,7 +1563,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, /* The previous buffer is not processed yet, tack on. */ qperf_inc(q, fast_requeue); } else { - rc = qdio_kick_outbound_q(q, 0); + rc = qdio_kick_outbound_q(q, count, 0); } /* Let drivers implement their own completion scanning: */ -- cgit v1.2.3-59-g8ed1b From 8b664cd127a1e3777e23c8aaa96ba52ef741bb55 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:15 +0100 Subject: s390/qeth: use IQD Multi-Write For IQD devices with Multi-Write support, we can defer the queue-flush further and transmit multiple IO buffers with a single TX doorbell. The same-target restriction still applies. Signed-off-by: Julian Wiedmann Reviewed-by: Alexandra Winter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 9 +++++++ drivers/s390/net/qeth_core_main.c | 54 +++++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 10 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index e4b55f9aa062..d08154502b15 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -532,6 +532,8 @@ struct qeth_qdio_out_q { struct timer_list timer; struct qeth_hdr *prev_hdr; u8 bulk_start; + u8 bulk_count; + u8 bulk_max; }; #define qeth_for_each_output_queue(card, q, i) \ @@ -878,6 +880,13 @@ static inline u16 qeth_iqd_translate_txq(struct net_device *dev, u16 txq) return txq; } +static inline bool qeth_iqd_is_mcast_queue(struct qeth_card *card, + struct qeth_qdio_out_q *queue) +{ + return qeth_iqd_translate_txq(card->dev, queue->queue_no) == + QETH_IQD_MCAST_TXQ; +} + static inline void qeth_scrub_qdio_buffer(struct qdio_buffer *buf, unsigned int elements) { diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index dda274351c21..62054afd73cc 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2634,6 +2634,18 @@ static int qeth_init_input_buffer(struct qeth_card *card, return 0; } +static unsigned int qeth_tx_select_bulk_max(struct qeth_card *card, + struct qeth_qdio_out_q *queue) +{ + if (!IS_IQD(card) || + qeth_iqd_is_mcast_queue(card, queue) || + card->options.cq == QETH_CQ_ENABLED || + qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd)) + return 1; + + return card->ssqd.mmwc ? card->ssqd.mmwc : 1; +} + int qeth_init_qdio_queues(struct qeth_card *card) { unsigned int i; @@ -2673,6 +2685,8 @@ int qeth_init_qdio_queues(struct qeth_card *card) queue->do_pack = 0; queue->prev_hdr = NULL; queue->bulk_start = 0; + queue->bulk_count = 0; + queue->bulk_max = qeth_tx_select_bulk_max(card, queue); atomic_set(&queue->used_buffers, 0); atomic_set(&queue->set_pci_flags_count, 0); atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); @@ -3318,10 +3332,11 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, static void qeth_flush_queue(struct qeth_qdio_out_q *queue) { - qeth_flush_buffers(queue, queue->bulk_start, 1); + qeth_flush_buffers(queue, queue->bulk_start, queue->bulk_count); - queue->bulk_start = QDIO_BUFNR(queue->bulk_start + 1); + queue->bulk_start = QDIO_BUFNR(queue->bulk_start + queue->bulk_count); queue->prev_hdr = NULL; + queue->bulk_count = 0; } static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue) @@ -3680,10 +3695,10 @@ check_layout: } static bool qeth_iqd_may_bulk(struct qeth_qdio_out_q *queue, - struct qeth_qdio_out_buffer *buffer, struct sk_buff *curr_skb, struct qeth_hdr *curr_hdr) { + struct qeth_qdio_out_buffer *buffer = queue->bufs[queue->bulk_start]; struct qeth_hdr *prev_hdr = queue->prev_hdr; if (!prev_hdr) @@ -3803,13 +3818,14 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, struct qeth_hdr *hdr, unsigned int offset, unsigned int hd_len) { - struct qeth_qdio_out_buffer *buffer = queue->bufs[queue->bulk_start]; unsigned int bytes = qdisc_pkt_len(skb); + struct qeth_qdio_out_buffer *buffer; unsigned int next_element; struct netdev_queue *txq; bool stopped = false; bool flush; + buffer = queue->bufs[QDIO_BUFNR(queue->bulk_start + queue->bulk_count)]; txq = netdev_get_tx_queue(card->dev, skb_get_queue_mapping(skb)); /* Just a sanity check, the wake/stop logic should ensure that we always @@ -3818,11 +3834,23 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) return -EBUSY; - if ((buffer->next_element_to_fill + elements > queue->max_elements) || - !qeth_iqd_may_bulk(queue, buffer, skb, hdr)) { - atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - qeth_flush_queue(queue); - buffer = queue->bufs[queue->bulk_start]; + flush = !qeth_iqd_may_bulk(queue, skb, hdr); + + if (flush || + (buffer->next_element_to_fill + elements > queue->max_elements)) { + if (buffer->next_element_to_fill > 0) { + atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); + queue->bulk_count++; + } + + if (queue->bulk_count >= queue->bulk_max) + flush = true; + + if (flush) + qeth_flush_queue(queue); + + buffer = queue->bufs[QDIO_BUFNR(queue->bulk_start + + queue->bulk_count)]; /* Sanity-check again: */ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) @@ -3848,7 +3876,13 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (flush || next_element >= queue->max_elements) { atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - qeth_flush_queue(queue); + queue->bulk_count++; + + if (queue->bulk_count >= queue->bulk_max) + flush = true; + + if (flush) + qeth_flush_queue(queue); } if (stopped && !qeth_out_queue_is_full(queue)) -- cgit v1.2.3-59-g8ed1b From ec2b559d6849096e111056fb79015d7a60f53e33 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:16 +0100 Subject: s390/qeth: use QDIO_BUFNR() qdio.h recently gained a new helper macro that handles wrap-around on a QDIO queue, consistently use it across all of qeth. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 62054afd73cc..52e3adb7864a 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3121,7 +3121,7 @@ static void qeth_queue_input_buffer(struct qeth_card *card, int index) for (i = queue->next_buf_to_init; i < queue->next_buf_to_init + count; ++i) { if (qeth_init_input_buffer(card, - &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { + &queue->bufs[QDIO_BUFNR(i)])) { break; } else { newcount++; @@ -3163,8 +3163,8 @@ static void qeth_queue_input_buffer(struct qeth_card *card, int index) if (rc) { QETH_CARD_TEXT(card, 2, "qinberr"); } - queue->next_buf_to_init = (queue->next_buf_to_init + count) % - QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_init = QDIO_BUFNR(queue->next_buf_to_init + + count); } } @@ -3212,7 +3212,7 @@ static int qeth_prep_flush_pack_buffer(struct qeth_qdio_out_q *queue) /* it's a packing buffer */ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); queue->next_buf_to_fill = - (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(queue->next_buf_to_fill + 1); return 1; } return 0; @@ -3266,7 +3266,8 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, unsigned int qdio_flags; for (i = index; i < index + count; ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; + unsigned int bidx = QDIO_BUFNR(i); + buf = queue->bufs[bidx]; buf->buffer->element[buf->next_element_to_fill - 1].eflags |= SBAL_EFLAGS_LAST_ENTRY; @@ -3434,8 +3435,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, } for (i = first_element; i < first_element + count; ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; - struct qdio_buffer *buffer = cq->qdio_bufs[bidx]; + struct qdio_buffer *buffer = cq->qdio_bufs[QDIO_BUFNR(i)]; int e = 0; while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && @@ -3456,8 +3456,8 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, "QDIO reported an error, rc=%i\n", rc); QETH_CARD_TEXT(card, 2, "qcqherr"); } - card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init - + count) % QDIO_MAX_BUFFERS_PER_Q; + + cq->next_buf_to_init = QDIO_BUFNR(cq->next_buf_to_init + count); } static void qeth_qdio_input_handler(struct ccw_device *ccwdev, @@ -3483,7 +3483,6 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, { struct qeth_card *card = (struct qeth_card *) card_ptr; struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; - struct qeth_qdio_out_buffer *buffer; struct net_device *dev = card->dev; struct netdev_queue *txq; int i; @@ -3497,10 +3496,10 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, } for (i = first_element; i < (first_element + count); ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; - buffer = queue->bufs[bidx]; - qeth_handle_send_error(card, buffer, qdio_error); - qeth_clear_output_buffer(queue, buffer, qdio_error, 0); + struct qeth_qdio_out_buffer *buf = queue->bufs[QDIO_BUFNR(i)]; + + qeth_handle_send_error(card, buf, qdio_error); + qeth_clear_output_buffer(queue, buf, qdio_error, 0); } atomic_sub(count, &queue->used_buffers); @@ -3932,8 +3931,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); flush_count++; queue->next_buf_to_fill = - (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(queue->next_buf_to_fill + 1); buffer = queue->bufs[queue->next_buf_to_fill]; /* We stepped forward, so sanity-check again: */ @@ -3966,8 +3964,8 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (!queue->do_pack || stopped || next_element >= queue->max_elements) { flush_count++; atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_fill = + QDIO_BUFNR(queue->next_buf_to_fill + 1); } if (flush_count) @@ -5199,8 +5197,7 @@ int qeth_poll(struct napi_struct *napi, int budget) card->rx.b_count--; if (card->rx.b_count) { card->rx.b_index = - (card->rx.b_index + 1) % - QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(card->rx.b_index + 1); card->rx.b_element = &card->qdio.in_q ->bufs[card->rx.b_index] -- cgit v1.2.3-59-g8ed1b From f9ce416ac79dce142fefd24422c9bf41b60fc6cc Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:17 +0100 Subject: s390/qeth: keep IRQ disabled until NAPI is really done When napi_complete_done() returns false, the NAPI instance is still active and we can keep the IRQ disabled a little longer. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 52e3adb7864a..d4e66707a03f 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5213,9 +5213,9 @@ int qeth_poll(struct napi_struct *napi, int budget) } } - napi_complete_done(napi, work_done); - if (qdio_start_irq(card->data.ccwdev, 0)) - napi_schedule(&card->napi); + if (napi_complete_done(napi, work_done) && + qdio_start_irq(CARD_DDEV(card), 0)) + napi_schedule(napi); out: return work_done; } -- cgit v1.2.3-59-g8ed1b From 9897d583b01525c6fb5dcaaa87263e7f395c905c Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:18 +0100 Subject: s390/qeth: consolidate some duplicated HW cmd code When setting a device online, both subdrivers have the same code to program the HW trap and Isolation mode. Move that code into a single place. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 10 +++++++++- drivers/s390/net/qeth_l2_main.c | 21 --------------------- drivers/s390/net/qeth_l3_main.c | 9 --------- 3 files changed, 9 insertions(+), 31 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d4e66707a03f..d51dcb3c5a01 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4293,7 +4293,6 @@ int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) } return rc; } -EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); void qeth_tx_timeout(struct net_device *dev) { @@ -5009,6 +5008,15 @@ retriable: goto out; } } + + if (!qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP) || + (card->info.hwtrap && qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))) + card->info.hwtrap = 0; + + rc = qeth_set_access_ctrl_online(card, 0); + if (rc) + goto out; + return 0; out: dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index bd8143e51747..8f3093d24b12 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -759,14 +759,6 @@ add_napi: return rc; } -static int qeth_l2_start_ipassists(struct qeth_card *card) -{ - /* configure isolation level */ - if (qeth_set_access_ctrl_online(card, 0)) - return -ENODEV; - return 0; -} - static void qeth_l2_trace_features(struct qeth_card *card) { /* Set BridgePort features */ @@ -797,13 +789,6 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) goto out_remove; } - if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { - if (card->info.hwtrap && - qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) - card->info.hwtrap = 0; - } else - card->info.hwtrap = 0; - qeth_bridgeport_query_support(card); if (card->options.sbp.supported_funcs) dev_info(&card->gdev->dev, @@ -825,12 +810,6 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) /* softsetup */ QETH_CARD_TEXT(card, 2, "softsetp"); - if (IS_OSD(card) || IS_OSX(card)) { - rc = qeth_l2_start_ipassists(card); - if (rc) - goto out_remove; - } - rc = qeth_init_qdio_queues(card); if (rc) { QETH_CARD_TEXT_(card, 2, "6err%d", rc); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index d7bfc7a0e4c0..acae44a699ad 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -953,8 +953,6 @@ static int qeth_l3_start_ipassists(struct qeth_card *card) { QETH_CARD_TEXT(card, 3, "strtipas"); - if (qeth_set_access_ctrl_online(card, 0)) - return -EIO; qeth_l3_start_ipa_arp_processing(card); /* go on*/ qeth_l3_start_ipa_source_mac(card); /* go on*/ qeth_l3_start_ipa_vlan(card); /* go on*/ @@ -2313,13 +2311,6 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev) goto out_remove; } - if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { - if (card->info.hwtrap && - qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) - card->info.hwtrap = 0; - } else - card->info.hwtrap = 0; - card->state = CARD_STATE_HARDSETUP; qeth_print_status_message(card); -- cgit v1.2.3-59-g8ed1b From 04fa55fe07d15587b00ef1269fcbf793a2e45739 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:19 +0100 Subject: s390/qeth: don't set card state in qeth_qdio_clear_card() Any change to the card state should only be driven by qeth_l?_set_online() and qeth_l?_stop_card(). qeth_qdio_clear_card() currently also gets called from (a) qeth_core_shutdown(), where we haven't walked through the whole teardown sequence. So changing the state to DOWN is not accurate. (b) qeth_core_hardsetup_card(), which is only called while the card is still in DOWN state. No change in behaviour here. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d51dcb3c5a01..9e8bd8e08146 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1513,7 +1513,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) rc = qeth_clear_halt_card(card, use_halt); if (rc) QETH_CARD_TEXT_(card, 3, "2err%d", rc); - card->state = CARD_STATE_DOWN; return rc; } EXPORT_SYMBOL_GPL(qeth_qdio_clear_card); -- cgit v1.2.3-59-g8ed1b From 1b40d4b2fbd626cbb789184d993d7452892fba3f Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:20 +0100 Subject: s390/qeth: use helpers for IP address hashing Replace our custom implementations with the stack's version of IP address hashing. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 21 ++++++--------------- drivers/s390/net/qeth_l3_main.c | 8 ++++---- 2 files changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 87659cfc9066..b7ba404a81f9 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -37,7 +37,7 @@ struct qeth_ipaddr { enum qeth_prot_versions proto; union { struct { - unsigned int addr; + __be32 addr; unsigned int mask; } a4; struct { @@ -89,21 +89,12 @@ static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1, return a1->u.a4.mask == a2->u.a4.mask; } -static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) +static inline u32 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) { - u64 ret = 0; - u8 *point; - - if (addr->proto == QETH_PROT_IPV6) { - point = (u8 *) &addr->u.a6.addr; - ret = get_unaligned((u64 *)point) ^ - get_unaligned((u64 *) (point + 8)); - } - if (addr->proto == QETH_PROT_IPV4) { - point = (u8 *) &addr->u.a4.addr; - ret = get_unaligned((u32 *) point); - } - return ret; + if (addr->proto == QETH_PROT_IPV6) + return ipv6_addr_hash(&addr->u.a6.addr); + else + return ipv4_addr_hash(addr->u.a4.addr); } struct qeth_ipato_entry { diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index acae44a699ad..8f92407bf580 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -76,7 +76,7 @@ static struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions prot) static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, struct qeth_ipaddr *query) { - u64 key = qeth_l3_ipaddr_hash(query); + u32 key = qeth_l3_ipaddr_hash(query); struct qeth_ipaddr *addr; if (query->is_multicast) { @@ -1128,7 +1128,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) { ip_eth_mc_map(im4->multiaddr, tmp->mac); - tmp->u.a4.addr = be32_to_cpu(im4->multiaddr); + tmp->u.a4.addr = im4->multiaddr; tmp->is_multicast = 1; ipm = qeth_l3_find_addr_by_ip(card, tmp); @@ -1140,7 +1140,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) if (!ipm) continue; ether_addr_copy(ipm->mac, tmp->mac); - ipm->u.a4.addr = be32_to_cpu(im4->multiaddr); + ipm->u.a4.addr = im4->multiaddr; ipm->is_multicast = 1; ipm->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->ip_mc_htable, @@ -2548,7 +2548,7 @@ static int qeth_l3_ip_event(struct notifier_block *this, QETH_CARD_TEXT(card, 3, "ipevent"); qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); - addr.u.a4.addr = be32_to_cpu(ifa->ifa_address); + addr.u.a4.addr = ifa->ifa_address; addr.u.a4.mask = be32_to_cpu(ifa->ifa_mask); return qeth_l3_handle_ip_event(card, &addr, event); -- cgit v1.2.3-59-g8ed1b From 8bf70b68847a1960e50ee8639580950b43b0e185 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 31 Oct 2019 13:42:21 +0100 Subject: s390/qeth: don't cache MAC addresses for multicast IPs Instead of storing the multicast-mapped MAC address in an IP address object, just calculate the MAC address when actually building a cmd for the IP address. While at it, also clean up some rather verbose copying of IP addresses. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_mpc.h | 4 ++-- drivers/s390/net/qeth_l3.h | 3 --- drivers/s390/net/qeth_l3_main.c | 24 ++++++++++-------------- 3 files changed, 12 insertions(+), 19 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 6420b58cf42b..9ad0d6f9d48b 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -11,6 +11,7 @@ #include #include +#include #define IPA_PDU_HEADER_SIZE 0x40 #define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer + 0x0e) @@ -365,8 +366,7 @@ struct qeth_ipacmd_setdelip6 { struct qeth_ipacmd_setdelipm { __u8 mac[6]; __u8 padding[2]; - __u8 ip6[12]; - __u8 ip4[4]; + struct in6_addr ip; } __attribute__ ((packed)); struct qeth_ipacmd_layer2setdelmac { diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index b7ba404a81f9..ba913d1ab88d 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -24,7 +24,6 @@ enum qeth_ip_types { struct qeth_ipaddr { struct hlist_node hnode; enum qeth_ip_types type; - unsigned char mac[ETH_ALEN]; u8 is_multicast:1; u8 in_progress:1; u8 disp_flag:2; @@ -74,12 +73,10 @@ static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1, * so 'proto' and 'addr' match for sure. * * For ucast: - * - 'mac' is always 0. * - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching * values are required to avoid mixups in takeover eligibility. * * For mcast, - * - 'mac' is mapped from the IP, and thus always matches. * - 'mask'/'pfxlen' is always 0. */ if (a1->type != a2->type) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8f92407bf580..70d4586dc779 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -381,12 +381,13 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card, if (!iob) return -ENOMEM; cmd = __ipa_cmd(iob); - ether_addr_copy(cmd->data.setdelipm.mac, addr->mac); - if (addr->proto == QETH_PROT_IPV6) - memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr, - sizeof(struct in6_addr)); - else - memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr, 4); + if (addr->proto == QETH_PROT_IPV6) { + cmd->data.setdelipm.ip = addr->u.a6.addr; + ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac); + } else { + cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr; + ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac); + } return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); } @@ -1127,7 +1128,6 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) { - ip_eth_mc_map(im4->multiaddr, tmp->mac); tmp->u.a4.addr = im4->multiaddr; tmp->is_multicast = 1; @@ -1139,7 +1139,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); if (!ipm) continue; - ether_addr_copy(ipm->mac, tmp->mac); + ipm->u.a4.addr = im4->multiaddr; ipm->is_multicast = 1; ipm->disp_flag = QETH_DISP_ADDR_ADD; @@ -1207,9 +1207,7 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, return; for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { - ipv6_eth_mc_map(&im6->mca_addr, tmp->mac); - memcpy(&tmp->u.a6.addr, &im6->mca_addr.s6_addr, - sizeof(struct in6_addr)); + tmp->u.a6.addr = im6->mca_addr; tmp->is_multicast = 1; ipm = qeth_l3_find_addr_by_ip(card, tmp); @@ -1223,9 +1221,7 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, if (!ipm) continue; - ether_addr_copy(ipm->mac, tmp->mac); - memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr, - sizeof(struct in6_addr)); + ipm->u.a6.addr = im6->mca_addr; ipm->is_multicast = 1; ipm->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->ip_mc_htable, -- cgit v1.2.3-59-g8ed1b From 845ef9047b1f4e8ea3b8865140066b08fe93d05c Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:14 +0100 Subject: s390/qeth: gather more detailed RX dropped/error statistics Where available, use the fine-grained counters in rtnl_link_stats64 to indicate different RX error causes. For drop reasons, use driver-private ethtool counters. In particular this patch allows us to keep track of driver-side drops due to unknown/unsupported HW descriptor format. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 7 +++++-- drivers/s390/net/qeth_core_main.c | 17 +++++++++++------ drivers/s390/net/qeth_ethtool.c | 2 ++ drivers/s390/net/qeth_l2_main.c | 1 + drivers/s390/net/qeth_l3_main.c | 1 + 5 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d08154502b15..14edc892f7c1 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -477,12 +477,15 @@ struct qeth_card_stats { u64 rx_sg_frags; u64 rx_sg_alloc_page; + u64 rx_dropped_nomem; + u64 rx_dropped_notsupp; + /* rtnl_link_stats64 */ u64 rx_packets; u64 rx_bytes; - u64 rx_errors; - u64 rx_dropped; u64 rx_multicast; + u64 rx_length_errors; + u64 rx_fifo_errors; }; struct qeth_out_q_stats { diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9e8bd8e08146..4e113f359be9 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3093,7 +3093,7 @@ static int qeth_check_qdio_errors(struct qeth_card *card, buf->element[14].sflags); QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); if ((buf->element[15].sflags) == 0x12) { - QETH_CARD_STAT_INC(card, rx_dropped); + QETH_CARD_STAT_INC(card, rx_fifo_errors); return 0; } else return 1; @@ -4346,7 +4346,7 @@ static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) case MII_NWAYTEST: /* N-way auto-neg test register */ break; case MII_RERRCOUNTER: /* rx error counter */ - rc = card->stats.rx_errors; + rc = card->stats.rx_length_errors + card->stats.rx_fifo_errors; break; case MII_SREVISION: /* silicon revision */ break; @@ -5092,6 +5092,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, headroom = sizeof(struct qeth_hdr); break; default: + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); break; } @@ -5134,7 +5135,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "unexeob"); QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); dev_kfree_skb_any(skb); - QETH_CARD_STAT_INC(card, rx_errors); + QETH_CARD_STAT_INC(card, rx_length_errors); return NULL; } element++; @@ -5156,7 +5157,7 @@ no_mem: if (net_ratelimit()) { QETH_CARD_TEXT(card, 2, "noskbmem"); } - QETH_CARD_STAT_INC(card, rx_dropped); + QETH_CARD_STAT_INC(card, rx_dropped_nomem); return NULL; } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); @@ -6236,9 +6237,13 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->rx_packets = card->stats.rx_packets; stats->rx_bytes = card->stats.rx_bytes; - stats->rx_errors = card->stats.rx_errors; - stats->rx_dropped = card->stats.rx_dropped; + stats->rx_errors = card->stats.rx_length_errors + + card->stats.rx_fifo_errors; + stats->rx_dropped = card->stats.rx_dropped_nomem + + card->stats.rx_dropped_notsupp; stats->multicast = card->stats.rx_multicast; + stats->rx_length_errors = card->stats.rx_length_errors; + stats->rx_fifo_errors = card->stats.rx_fifo_errors; for (i = 0; i < card->qdio.no_out_queues; i++) { queue = card->qdio.out_qs[i]; diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index 096698df3886..f7485c6dea25 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -49,6 +49,8 @@ static const struct qeth_stats card_stats[] = { QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs), QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags), QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page), + QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem), + QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp), }; #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8f3093d24b12..1e85956f95c6 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -336,6 +336,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, dev_kfree_skb_any(skb); QETH_CARD_TEXT(card, 3, "inbunkno"); QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); continue; } work_done++; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 70d4586dc779..72c61faae3f9 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1391,6 +1391,7 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, dev_kfree_skb_any(skb); QETH_CARD_TEXT(card, 3, "inbunkno"); QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); continue; } work_done++; -- cgit v1.2.3-59-g8ed1b From 5fd3fcbb8af8f9bc82afd84937393c193b95c204 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:15 +0100 Subject: s390/qeth: support per-frame invalidation Each RX buffer may contain up to 64KB worth of data. In case the device needs to discard a packet _after_ already having reserved space for it in the buffer, the whole buffer gets set to ERROR state. As the buffer might contain any number of good packets, this can result in collateral packet loss. qeth can provide relief by enabling per-frame invalidation. The RX buffer is then presented as usual, we just need to spot & drop any individual packet that was flagged as invalid. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 ++ drivers/s390/net/qeth_core_main.c | 13 +++++++++++-- drivers/s390/net/qeth_core_mpc.h | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 14edc892f7c1..52fd3c4bb132 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -368,6 +368,7 @@ enum qeth_header_ids { QETH_HEADER_TYPE_L3_TSO = 0x03, QETH_HEADER_TYPE_OSN = 0x04, QETH_HEADER_TYPE_L2_TSO = 0x06, + QETH_HEADER_MASK_INVAL = 0x80, }; /* flags for qeth_hdr.ext_flags */ #define QETH_HDR_EXT_VLAN_FRAME 0x01 @@ -485,6 +486,7 @@ struct qeth_card_stats { u64 rx_bytes; u64 rx_multicast; u64 rx_length_errors; + u64 rx_frame_errors; u64 rx_fifo_errors; }; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 4e113f359be9..c52241df980b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1956,6 +1956,7 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card, ccw_device_get_id(CARD_DDEV(card), &dev_id); iob->finalize = qeth_idx_finalize_cmd; + port |= QETH_IDX_ACT_INVAL_FRAME; memcpy(QETH_IDX_ACT_PNO(iob->data), &port, 1); memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); @@ -4346,7 +4347,9 @@ static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) case MII_NWAYTEST: /* N-way auto-neg test register */ break; case MII_RERRCOUNTER: /* rx error counter */ - rc = card->stats.rx_length_errors + card->stats.rx_fifo_errors; + rc = card->stats.rx_length_errors + + card->stats.rx_frame_errors + + card->stats.rx_fifo_errors; break; case MII_SREVISION: /* silicon revision */ break; @@ -5092,7 +5095,11 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, headroom = sizeof(struct qeth_hdr); break; default: - QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + if ((*hdr)->hdr.l2.id & QETH_HEADER_MASK_INVAL) + QETH_CARD_STAT_INC(card, rx_frame_errors); + else + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + break; } @@ -6238,11 +6245,13 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->rx_packets = card->stats.rx_packets; stats->rx_bytes = card->stats.rx_bytes; stats->rx_errors = card->stats.rx_length_errors + + card->stats.rx_frame_errors + card->stats.rx_fifo_errors; stats->rx_dropped = card->stats.rx_dropped_nomem + card->stats.rx_dropped_notsupp; stats->multicast = card->stats.rx_multicast; stats->rx_length_errors = card->stats.rx_length_errors; + stats->rx_frame_errors = card->stats.rx_frame_errors; stats->rx_fifo_errors = card->stats.rx_fifo_errors; for (i = 0; i < card->qdio.no_out_queues; i++) { diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 9ad0d6f9d48b..53fcf6641154 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -900,6 +900,7 @@ extern unsigned char IDX_ACTIVATE_WRITE[]; #define IDX_ACTIVATE_SIZE 0x22 #define QETH_IDX_ACT_PNO(buffer) (buffer+0x0b) #define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer + 0x0c) +#define QETH_IDX_ACT_INVAL_FRAME 0x40 #define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b] & 0x80) #define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer + 0x10) #define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer + 0x16) -- cgit v1.2.3-59-g8ed1b From 7d4faee7c6db9ddfb2b4de637dc6f1576f780bd7 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:16 +0100 Subject: s390/qeth: drop unwanted packets earlier in RX path Packets with an unexpected HW format are currently first extracted from the RX buffer, passed upwards to the layer-specific driver and only then finally dropped. Enhance the RX path so that we can drop such packets before even allocating an skb. For this, add some additional logic so that when a packet is meant to be dropped, we can still walk along the packet's data chunks in the RX buffer. This allows us to extract the following packet(s) from the buffer. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 32 ++++++++++++++++++++++++++++---- drivers/s390/net/qeth_l2_main.c | 27 ++++++++------------------- drivers/s390/net/qeth_l3_main.c | 26 ++++++++------------------ 3 files changed, 44 insertions(+), 41 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c52241df980b..467a9173058c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5072,6 +5072,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, int headroom = 0; int use_rx_sg = 0; +next_packet: /* qeth_hdr must not cross element boundaries */ while (element->length < offset + sizeof(struct qeth_hdr)) { if (qeth_is_last_sbale(element)) @@ -5088,10 +5089,22 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, break; case QETH_HEADER_TYPE_LAYER3: skb_len = (*hdr)->hdr.l3.length; + if (!IS_LAYER3(card)) { + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + skb = NULL; + goto walk_packet; + } + headroom = ETH_HLEN; break; case QETH_HEADER_TYPE_OSN: skb_len = (*hdr)->hdr.osn.pdu_length; + if (!IS_OSN(card)) { + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + skb = NULL; + goto walk_packet; + } + headroom = sizeof(struct qeth_hdr); break; default: @@ -5100,7 +5113,8 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, else QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - break; + /* Can't determine packet length, drop the whole buffer. */ + return NULL; } if (!skb_len) @@ -5126,10 +5140,12 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, if (headroom) skb_reserve(skb, headroom); +walk_packet: data_ptr = element->addr + offset; while (skb_len) { data_len = min(skb_len, (int)(element->length - offset)); - if (data_len) { + + if (skb && data_len) { if (use_rx_sg) qeth_create_skb_frag(element, skb, offset, data_len); @@ -5141,8 +5157,11 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, if (qeth_is_last_sbale(element)) { QETH_CARD_TEXT(card, 4, "unexeob"); QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); - dev_kfree_skb_any(skb); - QETH_CARD_STAT_INC(card, rx_length_errors); + if (skb) { + dev_kfree_skb_any(skb); + QETH_CARD_STAT_INC(card, + rx_length_errors); + } return NULL; } element++; @@ -5152,6 +5171,11 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, offset += data_len; } } + + /* This packet was skipped, go get another one: */ + if (!skb) + goto next_packet; + *__element = element; *__offset = offset; if (use_rx_sg) { diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 1e85956f95c6..ae69c981650d 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -315,30 +315,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, *done = 1; break; } - switch (hdr->hdr.l2.id) { - case QETH_HEADER_TYPE_LAYER2: + + if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { skb->protocol = eth_type_trans(skb, skb->dev); qeth_rx_csum(card, skb, hdr->hdr.l2.flags[1]); len = skb->len; napi_gro_receive(&card->napi, skb); - break; - case QETH_HEADER_TYPE_OSN: - if (IS_OSN(card)) { - skb_push(skb, sizeof(struct qeth_hdr)); - skb_copy_to_linear_data(skb, hdr, - sizeof(struct qeth_hdr)); - len = skb->len; - card->osn_info.data_cb(skb); - break; - } - /* Else, fall through */ - default: - dev_kfree_skb_any(skb); - QETH_CARD_TEXT(card, 3, "inbunkno"); - QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); - QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - continue; + } else { + skb_push(skb, sizeof(*hdr)); + skb_copy_to_linear_data(skb, hdr, sizeof(*hdr)); + len = skb->len; + card->osn_info.data_cb(skb); } + work_done++; budget--; QETH_CARD_STAT_INC(card, rx_packets); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 72c61faae3f9..3b901f3676c1 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1366,7 +1366,6 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, int work_done = 0; struct sk_buff *skb; struct qeth_hdr *hdr; - unsigned int len; *done = 0; WARN_ON_ONCE(!budget); @@ -1378,26 +1377,17 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, *done = 1; break; } - switch (hdr->hdr.l3.id) { - case QETH_HEADER_TYPE_LAYER3: + + if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3) qeth_l3_rebuild_skb(card, skb, hdr); - /* fall through */ - case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */ - skb->protocol = eth_type_trans(skb, skb->dev); - len = skb->len; - napi_gro_receive(&card->napi, skb); - break; - default: - dev_kfree_skb_any(skb); - QETH_CARD_TEXT(card, 3, "inbunkno"); - QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); - QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - continue; - } + + skb->protocol = eth_type_trans(skb, skb->dev); + QETH_CARD_STAT_INC(card, rx_packets); + QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); + + napi_gro_receive(&card->napi, skb); work_done++; budget--; - QETH_CARD_STAT_INC(card, rx_packets); - QETH_CARD_STAT_ADD(card, rx_bytes, len); } return work_done; } -- cgit v1.2.3-59-g8ed1b From 17caeaa4766dc447d6669b0f195c3ead746386ba Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:17 +0100 Subject: s390/qeth: handle skb allocation error gracefully When current code fails to allocate an skb in the RX path, it drops the whole RX buffer. Considering the large number of packets that a single RX buffer might contain, this is quite drastic. Skip over the packet instead, and try to extract the next packet from the RX buffer. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 467a9173058c..08185f76a727 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5065,12 +5065,12 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, struct qdio_buffer_element *element = *__element; struct qdio_buffer *buffer = qethbuffer->buffer; int offset = *__offset; + bool use_rx_sg = false; + unsigned int headroom; struct sk_buff *skb; int skb_len = 0; void *data_ptr; int data_len; - int headroom = 0; - int use_rx_sg = 0; next_packet: /* qeth_hdr must not cross element boundaries */ @@ -5086,6 +5086,7 @@ next_packet: switch ((*hdr)->hdr.l2.id) { case QETH_HEADER_TYPE_LAYER2: skb_len = (*hdr)->hdr.l2.pkt_length; + headroom = 0; break; case QETH_HEADER_TYPE_LAYER3: skb_len = (*hdr)->hdr.l3.length; @@ -5120,11 +5121,10 @@ next_packet: if (!skb_len) return NULL; - if (((skb_len >= card->options.rx_sg_cb) && - !IS_OSN(card) && - (!atomic_read(&card->force_alloc_skb))) || - (card->options.cq == QETH_CQ_ENABLED)) - use_rx_sg = 1; + use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) || + ((skb_len >= card->options.rx_sg_cb) && + !atomic_read(&card->force_alloc_skb) && + !IS_OSN(card)); if (use_rx_sg && qethbuffer->rx_skb) { /* QETH_CQ_ENABLED only: */ @@ -5135,9 +5135,10 @@ next_packet: skb = napi_alloc_skb(&card->napi, linear + headroom); } + if (!skb) - goto no_mem; - if (headroom) + QETH_CARD_STAT_INC(card, rx_dropped_nomem); + else if (headroom) skb_reserve(skb, headroom); walk_packet: @@ -5184,12 +5185,6 @@ walk_packet: skb_shinfo(skb)->nr_frags); } return skb; -no_mem: - if (net_ratelimit()) { - QETH_CARD_TEXT(card, 2, "noskbmem"); - } - QETH_CARD_STAT_INC(card, rx_dropped_nomem); - return NULL; } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); -- cgit v1.2.3-59-g8ed1b From 8311c7a252e82f000077ae0612fc4843b078f980 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:18 +0100 Subject: s390/qeth: clean up error path in qeth_core_probe_device() qeth_core_free_card() is meant to be the counterpart of qeth_alloc_card() - but unfortunately was also picked as the place to free the QDIO queues. This gets messy when qeth_core_probe_device() fails during qeth_add_dbf_entry(). At this point the card->qdio.state is not initialized yet, so qeth_free_qdio_queues() ends up operating on uninitialized data. Luckily for now, the whole qeth_card struct is zero-allocated and the value of the QETH_QDIO_UNINITIALIZED enum is 0 as well. So there's no real impact from this bug at the moment, it's just really fragile. Clean this up by moving the qeth_free_qdio_queues() call up one level in the hierarchy. This way it doesn't get called from the error path. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 08185f76a727..f1f56e354516 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4855,7 +4855,6 @@ static void qeth_core_free_card(struct qeth_card *card) qeth_clean_channel(&card->data); qeth_put_cmd(card->read_cmd); destroy_workqueue(card->event_wq); - qeth_free_qdio_queues(card); unregister_service_level(&card->qeth_service_level); dev_set_drvdata(&card->gdev->dev, NULL); kfree(card); @@ -5768,6 +5767,8 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev) qeth_core_free_discipline(card); } + qeth_free_qdio_queues(card); + free_netdev(card->dev); qeth_core_free_card(card); put_device(&gdev->dev); -- cgit v1.2.3-59-g8ed1b From ddf28100ee1fa4fa5946dacdfe92d5c795a236e2 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:19 +0100 Subject: s390/qeth: fine-tune L3 mcast locking Push the inet6_dev locking down into the helper that actually needs it for walking the mc_list. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 3b901f3676c1..a8f18bf0c227 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1206,6 +1206,7 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, if (!tmp) return; + read_lock_bh(&in6_dev->lock); for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { tmp->u.a6.addr = im6->mca_addr; tmp->is_multicast = 1; @@ -1228,6 +1229,8 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); } + read_unlock_bh(&in6_dev->lock); + kfree(tmp); } @@ -1253,9 +1256,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) in_dev = in6_dev_get(netdev); if (!in_dev) continue; - read_lock_bh(&in_dev->lock); qeth_l3_add_mc6_to_hash(card, in_dev); - read_unlock_bh(&in_dev->lock); in6_dev_put(in_dev); } } @@ -1273,10 +1274,8 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) return; rcu_read_lock(); - read_lock_bh(&in6_dev->lock); qeth_l3_add_mc6_to_hash(card, in6_dev); qeth_l3_add_vlan_mc6(card); - read_unlock_bh(&in6_dev->lock); rcu_read_unlock(); in6_dev_put(in6_dev); } -- cgit v1.2.3-59-g8ed1b From 32a186c7f9588c843c26a1f0fbbf38f77a7b577a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:20 +0100 Subject: s390/qeth: remove gratuitious RX modeset Trust the IPv4/IPv6 code to properly remove its mcast addresses when a VLAN device is unregistered, and then also trigger an RX modeset whenever it's needed. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index a8f18bf0c227..0497ee46660b 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -39,7 +39,6 @@ static int qeth_l3_set_offline(struct ccwgroup_device *); -static void qeth_l3_set_rx_mode(struct net_device *dev); static int qeth_l3_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *); static int qeth_l3_deregister_addr_entry(struct qeth_card *, @@ -1297,7 +1296,6 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, QETH_CARD_TEXT_(card, 4, "kid:%d", vid); clear_bit(vid, card->active_vlans); - qeth_l3_set_rx_mode(dev); return 0; } -- cgit v1.2.3-59-g8ed1b From 611abe5165ca185bb64e0427c7efe2acdc9d5250 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:21 +0100 Subject: s390/qeth: consolidate L3 mcast registration code Current code processes each (VLAN) device twice - once to inspect the IPv4 mcast addresses, and then a second time to walk the IPv6 mcast addresses. Unify all this into a single helper, thus removing some checks and a duplicated VLAN lookup. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 120 ++++++++++------------------------------ 1 file changed, 30 insertions(+), 90 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 0497ee46660b..1e060331a8cc 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1113,17 +1113,28 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); } -static void -qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) +static void qeth_l3_add_mcast_rcu(struct net_device *dev, + struct qeth_card *card) { + struct qeth_ipaddr *tmp = NULL; + struct inet6_dev *in6_dev; + struct in_device *in4_dev; + struct qeth_ipaddr *ipm; struct ip_mc_list *im4; - struct qeth_ipaddr *tmp, *ipm; + struct ifmcaddr6 *im6; QETH_CARD_TEXT(card, 4, "addmc"); + if (!dev || !(dev->flags & IFF_UP)) + goto out; + tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); if (!tmp) - return; + goto out; + + in4_dev = __in_dev_get_rcu(dev); + if (!in4_dev) + goto walk_ipv6; for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) { @@ -1147,63 +1158,15 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) } } - kfree(tmp); -} - -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc(struct qeth_card *card) -{ - struct in_device *in_dev; - u16 vid; - - QETH_CARD_TEXT(card, 4, "addmcvl"); - - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), - vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = __in_dev_get_rcu(netdev); - if (!in_dev) - continue; - qeth_l3_add_mc_to_hash(card, in_dev); - } -} - -static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) -{ - struct in_device *in4_dev; - - QETH_CARD_TEXT(card, 4, "chkmcv4"); - - rcu_read_lock(); - in4_dev = __in_dev_get_rcu(card->dev); - if (in4_dev == NULL) - goto unlock; - qeth_l3_add_mc_to_hash(card, in4_dev); - qeth_l3_add_vlan_mc(card); -unlock: - rcu_read_unlock(); -} - -static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, - struct inet6_dev *in6_dev) -{ - struct qeth_ipaddr *ipm; - struct ifmcaddr6 *im6; - struct qeth_ipaddr *tmp; +walk_ipv6: + if (!qeth_is_supported(card, IPA_IPV6)) + goto out; - QETH_CARD_TEXT(card, 4, "addmc6"); + in6_dev = __in6_dev_get(dev); + if (!in6_dev) + goto out; - tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); - if (!tmp) - return; + qeth_l3_init_ipaddr(tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); read_lock_bh(&in6_dev->lock); for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { @@ -1230,13 +1193,13 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, } read_unlock_bh(&in6_dev->lock); +out: kfree(tmp); } /* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc6(struct qeth_card *card) +static void qeth_l3_add_vlan_mc(struct qeth_card *card) { - struct inet6_dev *in_dev; u16 vid; QETH_CARD_TEXT(card, 4, "admc6vl"); @@ -1249,36 +1212,10 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = in6_dev_get(netdev); - if (!in_dev) - continue; - qeth_l3_add_mc6_to_hash(card, in_dev); - in6_dev_put(in_dev); + qeth_l3_add_mcast_rcu(netdev, card); } } -static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) -{ - struct inet6_dev *in6_dev; - - QETH_CARD_TEXT(card, 4, "chkmcv6"); - - if (!qeth_is_supported(card, IPA_IPV6)) - return ; - in6_dev = in6_dev_get(card->dev); - if (!in6_dev) - return; - - rcu_read_lock(); - qeth_l3_add_mc6_to_hash(card, in6_dev); - qeth_l3_add_vlan_mc6(card); - rcu_read_unlock(); - in6_dev_put(in6_dev); -} - static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { @@ -1450,8 +1387,11 @@ static void qeth_l3_rx_mode_work(struct work_struct *work) QETH_CARD_TEXT(card, 3, "setmulti"); if (!card->options.sniffer) { - qeth_l3_add_multicast_ipv4(card); - qeth_l3_add_multicast_ipv6(card); + rcu_read_lock(); + qeth_l3_add_mcast_rcu(card->dev, card); + if (qeth_is_supported(card, IPA_FULL_VLAN)) + qeth_l3_add_vlan_mc(card); + rcu_read_unlock(); hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { switch (addr->disp_flag) { -- cgit v1.2.3-59-g8ed1b From 8659c189b6f2f0af7ae90867d497178ea45c8251 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:22 +0100 Subject: s390/qeth: remove VLAN tracking for L3 devices Use vlan_for_each() instead of tracking each registered VID internally. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_l3_main.c | 42 +++++++++++------------------------------ 2 files changed, 11 insertions(+), 32 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 52fd3c4bb132..f81f1523d528 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -824,7 +824,6 @@ struct qeth_card { struct workqueue_struct *event_wq; struct workqueue_struct *cmd_wq; wait_queue_head_t wait_q; - unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; DECLARE_HASHTABLE(mac_htable, 4); DECLARE_HASHTABLE(ip_htable, 4); struct mutex ip_lock; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 1e060331a8cc..f4c65971321a 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1113,10 +1113,10 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); } -static void qeth_l3_add_mcast_rcu(struct net_device *dev, - struct qeth_card *card) +static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) { struct qeth_ipaddr *tmp = NULL; + struct qeth_card *card = arg; struct inet6_dev *in6_dev; struct in_device *in4_dev; struct qeth_ipaddr *ipm; @@ -1132,12 +1132,12 @@ static void qeth_l3_add_mcast_rcu(struct net_device *dev, if (!tmp) goto out; - in4_dev = __in_dev_get_rcu(dev); + in4_dev = __in_dev_get_rtnl(dev); if (!in4_dev) goto walk_ipv6; - for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; - im4 = rcu_dereference(im4->next_rcu)) { + for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rtnl_dereference(im4->next_rcu)) { tmp->u.a4.addr = im4->multiaddr; tmp->is_multicast = 1; @@ -1195,25 +1195,7 @@ walk_ipv6: out: kfree(tmp); -} - -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc(struct qeth_card *card) -{ - u16 vid; - - QETH_CARD_TEXT(card, 4, "admc6vl"); - - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), - vid); - qeth_l3_add_mcast_rcu(netdev, card); - } + return 0; } static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, @@ -1221,7 +1203,7 @@ static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, { struct qeth_card *card = dev->ml_priv; - set_bit(vid, card->active_vlans); + QETH_CARD_TEXT_(card, 4, "aid:%d", vid); return 0; } @@ -1231,8 +1213,6 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, struct qeth_card *card = dev->ml_priv; QETH_CARD_TEXT_(card, 4, "kid:%d", vid); - - clear_bit(vid, card->active_vlans); return 0; } @@ -1387,11 +1367,11 @@ static void qeth_l3_rx_mode_work(struct work_struct *work) QETH_CARD_TEXT(card, 3, "setmulti"); if (!card->options.sniffer) { - rcu_read_lock(); - qeth_l3_add_mcast_rcu(card->dev, card); + rtnl_lock(); + qeth_l3_add_mcast_rtnl(card->dev, 0, card); if (qeth_is_supported(card, IPA_FULL_VLAN)) - qeth_l3_add_vlan_mc(card); - rcu_read_unlock(); + vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card); + rtnl_unlock(); hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { switch (addr->disp_flag) { -- cgit v1.2.3-59-g8ed1b From b80c08ac9414e33ac3b2591146f4f37fdefd3ecb Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:23 +0100 Subject: s390/qeth: replace qeth_l3_get_addr_buffer() The remaining usage effectively is a kmemdup() of the query object. By not wrapping it, some of the callers can now use GFP_KERNEL for the allocation. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 1 + drivers/s390/net/qeth_l3_main.c | 62 +++++++++++++++-------------------------- 2 files changed, 23 insertions(+), 40 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index ba913d1ab88d..2421f29021c1 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -54,6 +54,7 @@ static inline void qeth_l3_init_ipaddr(struct qeth_ipaddr *addr, addr->type = type; addr->proto = proto; addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + addr->ref_counter = 1; } static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index f4c65971321a..e7ce73b9f016 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -63,15 +63,6 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, qeth_l3_ipaddr6_to_string(addr, buf); } -static struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions prot) -{ - struct qeth_ipaddr *addr = kmalloc(sizeof(*addr), GFP_ATOMIC); - - if (addr) - qeth_l3_init_ipaddr(addr, QETH_IP_TYPE_NORMAL, prot); - return addr; -} - static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, struct qeth_ipaddr *query) { @@ -216,13 +207,10 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) "Registering IP address %s failed\n", buf); return -EADDRINUSE; } else { - addr = qeth_l3_get_addr_buffer(tmp_addr->proto); + addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL); if (!addr) return -ENOMEM; - memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr)); - addr->ref_counter = 1; - if (qeth_l3_is_addr_covered_by_ipato(card, addr)) { QETH_CARD_TEXT(card, 2, "tkovaddr"); addr->ipato = 1; @@ -1115,11 +1103,11 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) { - struct qeth_ipaddr *tmp = NULL; struct qeth_card *card = arg; struct inet6_dev *in6_dev; struct in_device *in4_dev; struct qeth_ipaddr *ipm; + struct qeth_ipaddr tmp; struct ip_mc_list *im4; struct ifmcaddr6 *im6; @@ -1128,34 +1116,31 @@ static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) if (!dev || !(dev->flags & IFF_UP)) goto out; - tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!tmp) - goto out; - in4_dev = __in_dev_get_rtnl(dev); if (!in4_dev) goto walk_ipv6; + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; + for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL; im4 = rtnl_dereference(im4->next_rcu)) { - tmp->u.a4.addr = im4->multiaddr; - tmp->is_multicast = 1; + tmp.u.a4.addr = im4->multiaddr; - ipm = qeth_l3_find_addr_by_ip(card, tmp); + ipm = qeth_l3_find_addr_by_ip(card, &tmp); if (ipm) { /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - } else { - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!ipm) - continue; - - ipm->u.a4.addr = im4->multiaddr; - ipm->is_multicast = 1; - ipm->disp_flag = QETH_DISP_ADDR_ADD; - hash_add(card->ip_mc_htable, - &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); + continue; } + + ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL); + if (!ipm) + continue; + + hash_add(card->ip_mc_htable, &ipm->hnode, + qeth_l3_ipaddr_hash(ipm)); } walk_ipv6: @@ -1166,27 +1151,25 @@ walk_ipv6: if (!in6_dev) goto out; - qeth_l3_init_ipaddr(tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; read_lock_bh(&in6_dev->lock); for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { - tmp->u.a6.addr = im6->mca_addr; - tmp->is_multicast = 1; + tmp.u.a6.addr = im6->mca_addr; - ipm = qeth_l3_find_addr_by_ip(card, tmp); + ipm = qeth_l3_find_addr_by_ip(card, &tmp); if (ipm) { /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; continue; } - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); + ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC); if (!ipm) continue; - ipm->u.a6.addr = im6->mca_addr; - ipm->is_multicast = 1; - ipm->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->ip_mc_htable, &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); @@ -1194,7 +1177,6 @@ walk_ipv6: read_unlock_bh(&in6_dev->lock); out: - kfree(tmp); return 0; } -- cgit v1.2.3-59-g8ed1b From 0b81c6c620215743204cc03df40b25662a97a263 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 14 Nov 2019 11:19:24 +0100 Subject: s390/qeth: don't check drvdata in sysfs code Given the way how the sysfs attributes are registered / unregistered, the show/store helpers will never be called with a NULL drvdata. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_sys.c | 80 ++-------------------------------- drivers/s390/net/qeth_l2_sys.c | 29 ------------- drivers/s390/net/qeth_l3_sys.c | 94 ---------------------------------------- 3 files changed, 4 insertions(+), 199 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 9f392497d570..e81170ab6d9a 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -20,8 +20,6 @@ static ssize_t qeth_dev_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; switch (card->state) { case CARD_STATE_DOWN: @@ -45,8 +43,6 @@ static ssize_t qeth_dev_chpid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%02X\n", card->info.chpid); } @@ -57,8 +53,7 @@ static ssize_t qeth_dev_if_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; + return sprintf(buf, "%s\n", QETH_CARD_IFNAME(card)); } @@ -68,8 +63,6 @@ static ssize_t qeth_dev_card_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%s\n", qeth_get_cardname_short(card)); } @@ -94,8 +87,6 @@ static ssize_t qeth_dev_inbuf_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%s\n", qeth_get_bufsize_str(card)); } @@ -106,8 +97,6 @@ static ssize_t qeth_dev_portno_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%i\n", card->dev->dev_port); } @@ -120,9 +109,6 @@ static ssize_t qeth_dev_portno_store(struct device *dev, unsigned int portno, limit; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -171,9 +157,6 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - switch (card->qdio.do_prio_queueing) { case QETH_PRIO_Q_ING_PREC: return sprintf(buf, "%s\n", "by precedence"); @@ -195,9 +178,6 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); int rc = 0; - if (!card) - return -EINVAL; - if (IS_IQD(card)) return -EOPNOTSUPP; @@ -262,9 +242,6 @@ static ssize_t qeth_dev_bufcnt_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->qdio.in_buf_pool.buf_count); } @@ -276,9 +253,6 @@ static ssize_t qeth_dev_bufcnt_store(struct device *dev, int cnt, old_cnt; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -307,9 +281,6 @@ static ssize_t qeth_dev_recover_store(struct device *dev, char *tmp; int i; - if (!card) - return -EINVAL; - if (!qeth_card_hw_is_reachable(card)) return -EPERM; @@ -325,11 +296,6 @@ static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store); static ssize_t qeth_dev_performance_stats_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct qeth_card *card = dev_get_drvdata(dev); - - if (!card) - return -EINVAL; - return sprintf(buf, "1\n"); } @@ -342,9 +308,6 @@ static ssize_t qeth_dev_performance_stats_store(struct device *dev, bool reset; int rc; - if (!card) - return -EINVAL; - rc = kstrtobool(buf, &reset); if (rc) return rc; @@ -370,9 +333,6 @@ static ssize_t qeth_dev_layer2_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.layer); } @@ -385,9 +345,6 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, int i, rc = 0; enum qeth_discipline_id newdis; - if (!card) - return -EINVAL; - mutex_lock(&card->discipline_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -453,9 +410,6 @@ static ssize_t qeth_dev_isolation_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - switch (card->options.isolation) { case ISOLATION_MODE_NONE: return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE); @@ -475,9 +429,6 @@ static ssize_t qeth_dev_isolation_store(struct device *dev, enum qeth_ipa_isolation_modes isolation; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (!IS_OSD(card) && !IS_OSX(card)) { rc = -EOPNOTSUPP; @@ -522,9 +473,6 @@ static ssize_t qeth_dev_switch_attrs_show(struct device *dev, struct qeth_switch_info sw_info; int rc = 0; - if (!card) - return -EINVAL; - if (!qeth_card_hw_is_reachable(card)) return sprintf(buf, "n/a\n"); @@ -555,8 +503,6 @@ static ssize_t qeth_hw_trap_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; if (card->info.hwtrap) return snprintf(buf, 5, "arm\n"); else @@ -570,9 +516,6 @@ static ssize_t qeth_hw_trap_store(struct device *dev, int rc = 0; int state = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (qeth_card_hw_is_reachable(card)) state = 1; @@ -607,24 +550,12 @@ static ssize_t qeth_hw_trap_store(struct device *dev, static DEVICE_ATTR(hw_trap, 0644, qeth_hw_trap_show, qeth_hw_trap_store); -static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value) -{ - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", value); -} - static ssize_t qeth_dev_blkt_store(struct qeth_card *card, const char *buf, size_t count, int *value, int max_value) { char *tmp; int i, rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -645,7 +576,7 @@ static ssize_t qeth_dev_blkt_total_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, card->info.blkt.time_total); + return sprintf(buf, "%i\n", card->info.blkt.time_total); } static ssize_t qeth_dev_blkt_total_store(struct device *dev, @@ -657,8 +588,6 @@ static ssize_t qeth_dev_blkt_total_store(struct device *dev, &card->info.blkt.time_total, 5000); } - - static DEVICE_ATTR(total, 0644, qeth_dev_blkt_total_show, qeth_dev_blkt_total_store); @@ -667,7 +596,7 @@ static ssize_t qeth_dev_blkt_inter_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, card->info.blkt.inter_packet); + return sprintf(buf, "%i\n", card->info.blkt.inter_packet); } static ssize_t qeth_dev_blkt_inter_store(struct device *dev, @@ -687,8 +616,7 @@ static ssize_t qeth_dev_blkt_inter_jumbo_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, - card->info.blkt.inter_packet_jumbo); + return sprintf(buf, "%i\n", card->info.blkt.inter_packet_jumbo); } static ssize_t qeth_dev_blkt_inter_jumbo_store(struct device *dev, diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index f2c3b127b1e4..d9af5fe040be 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -18,9 +18,6 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, int rc = 0; char *word; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); @@ -79,8 +76,6 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev, int rc = 0; enum qeth_sbp_roles role; - if (!card) - return -EINVAL; if (sysfs_streq(buf, "primary")) role = QETH_SBP_ROLE_PRIMARY; else if (sysfs_streq(buf, "secondary")) @@ -132,9 +127,6 @@ static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); int enabled; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); @@ -150,9 +142,6 @@ static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, bool enable; int rc; - if (!card) - return -EINVAL; - rc = kstrtobool(buf, &enable); if (rc) return rc; @@ -183,9 +172,6 @@ static ssize_t qeth_bridgeport_reflect_show(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); char *state; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); @@ -207,9 +193,6 @@ static ssize_t qeth_bridgeport_reflect_store(struct device *dev, int enable, primary; int rc = 0; - if (!card) - return -EINVAL; - if (sysfs_streq(buf, "none")) { enable = 0; primary = 0; @@ -315,9 +298,6 @@ static ssize_t qeth_vnicc_timeout_show(struct device *dev, u32 timeout; int rc; - if (!card) - return -EINVAL; - rc = qeth_l2_vnicc_get_timeout(card, &timeout); if (rc == -EBUSY) return sprintf(buf, "n/a (BridgePort)\n"); @@ -335,9 +315,6 @@ static ssize_t qeth_vnicc_timeout_store(struct device *dev, u32 timeout; int rc; - if (!card) - return -EINVAL; - rc = kstrtou32(buf, 10, &timeout); if (rc) return rc; @@ -357,9 +334,6 @@ static ssize_t qeth_vnicc_char_show(struct device *dev, u32 vnicc; int rc; - if (!card) - return -EINVAL; - vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr->attr.name); rc = qeth_l2_vnicc_get_state(card, vnicc, &state); @@ -380,9 +354,6 @@ static ssize_t qeth_vnicc_char_store(struct device *dev, u32 vnicc; int rc; - if (!card) - return -EINVAL; - if (kstrtobool(buf, &state)) return -EINVAL; diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 2f73b33c9347..b6b38b69a5f4 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -60,9 +60,6 @@ static ssize_t qeth_l3_dev_route4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_show(card, &card->options.route4, buf); } @@ -109,9 +106,6 @@ static ssize_t qeth_l3_dev_route4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_store(card, &card->options.route4, QETH_PROT_IPV4, buf, count); } @@ -124,9 +118,6 @@ static ssize_t qeth_l3_dev_route6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_show(card, &card->options.route6, buf); } @@ -135,9 +126,6 @@ static ssize_t qeth_l3_dev_route6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_store(card, &card->options.route6, QETH_PROT_IPV6, buf, count); } @@ -150,9 +138,6 @@ static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); } @@ -163,9 +148,6 @@ static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev, char *tmp; int i, rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -190,9 +172,6 @@ static ssize_t qeth_l3_dev_sniffer_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0); } @@ -203,9 +182,6 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev, int rc = 0; unsigned long i; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; if (card->options.cq == QETH_CQ_ENABLED) @@ -248,16 +224,12 @@ out: static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show, qeth_l3_dev_sniffer_store); - static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); char tmp_hsuid[9]; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; @@ -273,9 +245,6 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, char *tmp; int rc; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; if (card->state != CARD_STATE_DOWN) @@ -336,9 +305,6 @@ static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); } @@ -349,9 +315,6 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, bool enable; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -385,9 +348,6 @@ static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); } @@ -399,9 +359,6 @@ static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev, bool invert; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (sysfs_streq(buf, "toggle")) { invert = !card->ipato.invert4; @@ -460,9 +417,6 @@ static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); } @@ -528,9 +482,6 @@ static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -558,9 +509,6 @@ static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -572,9 +520,6 @@ static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); } @@ -585,9 +530,6 @@ static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev, bool invert; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (sysfs_streq(buf, "toggle")) { invert = !card->ipato.invert6; @@ -617,9 +559,6 @@ static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); } @@ -628,9 +567,6 @@ static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -643,9 +579,6 @@ static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); } @@ -679,9 +612,6 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf, int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int i; - if (!card) - return -EINVAL; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len += 2; /* \n + terminator */ mutex_lock(&card->ip_lock); @@ -741,9 +671,6 @@ static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -771,9 +698,6 @@ static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -793,9 +717,6 @@ static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -808,9 +729,6 @@ static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); } @@ -884,9 +802,6 @@ static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -914,9 +829,6 @@ static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -936,9 +848,6 @@ static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -951,9 +860,6 @@ static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); } -- cgit v1.2.3-59-g8ed1b From 42bfba9eaa33dd4af0b50b87508062a41ec26653 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Thu, 14 Nov 2019 13:02:41 +0100 Subject: net/smc: immediate termination for SMCD link groups SMCD link group termination is called when peer signals its shutdown of its corresponding link group. For regular shutdowns no connections exist anymore. For abnormal shutdowns connections must be killed and their DMBs must be unregistered immediately. That means the SMCR method to delay the link group freeing several seconds does not fit. This patch adds immediate termination of a link group and its SMCD connections and makes sure all SMCD link group related cleanup steps are finished. Signed-off-by: Ursula Braun Signed-off-by: Karsten Graul Signed-off-by: David S. Miller --- drivers/s390/net/ism.h | 2 -- include/net/smc.h | 2 ++ net/smc/smc_close.c | 25 +++++++++++++++++++------ net/smc/smc_core.c | 46 +++++++++++++++++++++++++++++++++++++++------- net/smc/smc_ism.c | 14 ++++++++++++-- 5 files changed, 72 insertions(+), 17 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/ism.h b/drivers/s390/net/ism.h index 66eac2b9704d..1901e9c80ed8 100644 --- a/drivers/s390/net/ism.h +++ b/drivers/s390/net/ism.h @@ -32,8 +32,6 @@ #define ISM_UNREG_SBA 0x11 #define ISM_UNREG_IEQ 0x12 -#define ISM_ERROR 0xFFFF - struct ism_req_hdr { u32 cmd; u16 : 16; diff --git a/include/net/smc.h b/include/net/smc.h index 05174ae4f325..7c2082341bb3 100644 --- a/include/net/smc.h +++ b/include/net/smc.h @@ -37,6 +37,8 @@ struct smcd_dmb { #define ISM_EVENT_GID 1 #define ISM_EVENT_SWR 2 +#define ISM_ERROR 0xFFFF + struct smcd_event { u32 type; u32 code; diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c index d34e5adce2eb..d205b2114006 100644 --- a/net/smc/smc_close.c +++ b/net/smc/smc_close.c @@ -110,6 +110,17 @@ int smc_close_abort(struct smc_connection *conn) return smc_cdc_get_slot_and_msg_send(conn); } +static void smc_close_cancel_work(struct smc_sock *smc) +{ + struct sock *sk = &smc->sk; + + release_sock(sk); + cancel_work_sync(&smc->conn.close_work); + cancel_delayed_work_sync(&smc->conn.tx_work); + lock_sock(sk); + sk->sk_state = SMC_CLOSED; +} + /* terminate smc socket abnormally - active abort * link group is terminated, i.e. RDMA communication no longer possible */ @@ -126,23 +137,21 @@ void smc_close_active_abort(struct smc_sock *smc) switch (sk->sk_state) { case SMC_ACTIVE: sk->sk_state = SMC_PEERABORTWAIT; - release_sock(sk); - cancel_delayed_work_sync(&smc->conn.tx_work); - lock_sock(sk); + smc_close_cancel_work(smc); sk->sk_state = SMC_CLOSED; sock_put(sk); /* passive closing */ break; case SMC_APPCLOSEWAIT1: case SMC_APPCLOSEWAIT2: - release_sock(sk); - cancel_delayed_work_sync(&smc->conn.tx_work); - lock_sock(sk); + smc_close_cancel_work(smc); sk->sk_state = SMC_CLOSED; sock_put(sk); /* postponed passive closing */ break; case SMC_PEERCLOSEWAIT1: case SMC_PEERCLOSEWAIT2: case SMC_PEERFINCLOSEWAIT: + sk->sk_state = SMC_PEERABORTWAIT; + smc_close_cancel_work(smc); sk->sk_state = SMC_CLOSED; smc_conn_free(&smc->conn); release_clcsock = true; @@ -150,7 +159,11 @@ void smc_close_active_abort(struct smc_sock *smc) break; case SMC_PROCESSABORT: case SMC_APPFINCLOSEWAIT: + sk->sk_state = SMC_PEERABORTWAIT; + smc_close_cancel_work(smc); sk->sk_state = SMC_CLOSED; + smc_conn_free(&smc->conn); + release_clcsock = true; break; case SMC_INIT: case SMC_PEERABORTWAIT: diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 561f069b30de..9d6da2c7413d 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -214,7 +214,7 @@ static void smc_lgr_free_work(struct work_struct *work) if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE) smc_llc_link_inactive(lnk); - if (lgr->is_smcd) + if (lgr->is_smcd && !lgr->terminating) smc_ism_signal_shutdown(lgr); smc_lgr_free(lgr); } @@ -381,7 +381,8 @@ void smc_conn_free(struct smc_connection *conn) if (!lgr) return; if (lgr->is_smcd) { - smc_ism_unset_conn(conn); + if (!list_empty(&lgr->list)) + smc_ism_unset_conn(conn); tasklet_kill(&conn->rx_tsklet); } else { smc_cdc_tx_dismiss_slots(conn); @@ -481,8 +482,10 @@ static void smc_lgr_free(struct smc_link_group *lgr) { smc_lgr_free_bufs(lgr); if (lgr->is_smcd) { - smc_ism_put_vlan(lgr->smcd, lgr->vlan_id); - put_device(&lgr->smcd->dev); + if (!lgr->terminating) { + smc_ism_put_vlan(lgr->smcd, lgr->vlan_id); + put_device(&lgr->smcd->dev); + } } else { smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]); put_device(&lgr->lnk[SMC_SINGLE_LINK].smcibdev->ibdev->dev); @@ -503,6 +506,20 @@ void smc_lgr_forget(struct smc_link_group *lgr) spin_unlock_bh(lgr_lock); } +static void smcd_unregister_all_dmbs(struct smc_link_group *lgr) +{ + int i; + + for (i = 0; i < SMC_RMBE_SIZES; i++) { + struct smc_buf_desc *buf_desc; + + list_for_each_entry(buf_desc, &lgr->rmbs[i], list) { + buf_desc->len += sizeof(struct smcd_cdc_msg); + smc_ism_unregister_dmb(lgr->smcd, buf_desc); + } + } +} + static void smc_sk_wake_ups(struct smc_sock *smc) { smc->sk.sk_write_space(&smc->sk); @@ -522,12 +539,28 @@ static void smc_conn_kill(struct smc_connection *conn) conn->killed = 1; smc->sk.sk_err = ECONNABORTED; smc_sk_wake_ups(smc); - if (conn->lgr->is_smcd) + if (conn->lgr->is_smcd) { + smc_ism_unset_conn(conn); tasklet_kill(&conn->rx_tsklet); + } smc_lgr_unregister_conn(conn); smc_close_active_abort(smc); } +static void smc_lgr_cleanup(struct smc_link_group *lgr) +{ + if (lgr->is_smcd) { + smc_ism_signal_shutdown(lgr); + smcd_unregister_all_dmbs(lgr); + smc_ism_put_vlan(lgr->smcd, lgr->vlan_id); + put_device(&lgr->smcd->dev); + } else { + struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; + + wake_up(&lnk->wr_reg_wait); + } +} + /* terminate link group */ static void __smc_lgr_terminate(struct smc_link_group *lgr) { @@ -557,8 +590,7 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr) node = rb_first(&lgr->conns_all); } read_unlock_bh(&lgr->conns_lock); - if (!lgr->is_smcd) - wake_up(&lgr->lnk[SMC_SINGLE_LINK].wr_reg_wait); + smc_lgr_cleanup(lgr); smc_lgr_schedule_free_work_fast(lgr); } diff --git a/net/smc/smc_ism.c b/net/smc/smc_ism.c index 18946e95a3be..903da947b20d 100644 --- a/net/smc/smc_ism.c +++ b/net/smc/smc_ism.c @@ -146,6 +146,10 @@ out: int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) { struct smcd_dmb dmb; + int rc = 0; + + if (!dmb_desc->dma_addr) + return rc; memset(&dmb, 0, sizeof(dmb)); dmb.dmb_tok = dmb_desc->token; @@ -153,7 +157,13 @@ int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) dmb.cpu_addr = dmb_desc->cpu_addr; dmb.dma_addr = dmb_desc->dma_addr; dmb.dmb_len = dmb_desc->len; - return smcd->ops->unregister_dmb(smcd, &dmb); + rc = smcd->ops->unregister_dmb(smcd, &dmb); + if (!rc || rc == ISM_ERROR) { + dmb_desc->cpu_addr = NULL; + dmb_desc->dma_addr = 0; + } + + return rc; } int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len, @@ -375,7 +385,7 @@ void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno) spin_lock_irqsave(&smcd->lock, flags); conn = smcd->conn[dmbno]; - if (conn) + if (conn && !conn->killed) tasklet_schedule(&conn->rx_tsklet); spin_unlock_irqrestore(&smcd->lock, flags); } -- cgit v1.2.3-59-g8ed1b