diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-06-30 15:51:09 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-06-30 15:51:09 -0700 |
commit | dbe69e43372212527abf48609aba7fc39a6daa27 (patch) | |
tree | 96cfafdf70f5325ceeac1054daf7deca339c9730 /drivers/s390 | |
parent | a6eaf3850cb171c328a8b0db6d3c79286a1eba9d (diff) | |
parent | b6df00789e2831fff7a2c65aa7164b2a4dcbe599 (diff) |
Merge tag 'net-next-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Jakub Kicinski:
"Core:
- BPF:
- add syscall program type and libbpf support for generating
instructions and bindings for in-kernel BPF loaders (BPF loaders
for BPF), this is a stepping stone for signed BPF programs
- infrastructure to migrate TCP child sockets from one listener to
another in the same reuseport group/map to improve flexibility
of service hand-off/restart
- add broadcast support to XDP redirect
- allow bypass of the lockless qdisc to improving performance (for
pktgen: +23% with one thread, +44% with 2 threads)
- add a simpler version of "DO_ONCE()" which does not require jump
labels, intended for slow-path usage
- virtio/vsock: introduce SOCK_SEQPACKET support
- add getsocketopt to retrieve netns cookie
- ip: treat lowest address of a IPv4 subnet as ordinary unicast
address allowing reclaiming of precious IPv4 addresses
- ipv6: use prandom_u32() for ID generation
- ip: add support for more flexible field selection for hashing
across multi-path routes (w/ offload to mlxsw)
- icmp: add support for extended RFC 8335 PROBE (ping)
- seg6: add support for SRv6 End.DT46 behavior
- mptcp:
- DSS checksum support (RFC 8684) to detect middlebox meddling
- support Connection-time 'C' flag
- time stamping support
- sctp: packetization Layer Path MTU Discovery (RFC 8899)
- xfrm: speed up state addition with seq set
- WiFi:
- hidden AP discovery on 6 GHz and other HE 6 GHz improvements
- aggregation handling improvements for some drivers
- minstrel improvements for no-ack frames
- deferred rate control for TXQs to improve reaction times
- switch from round robin to virtual time-based airtime scheduler
- add trace points:
- tcp checksum errors
- openvswitch - action execution, upcalls
- socket errors via sk_error_report
Device APIs:
- devlink: add rate API for hierarchical control of max egress rate
of virtual devices (VFs, SFs etc.)
- don't require RCU read lock to be held around BPF hooks in NAPI
context
- page_pool: generic buffer recycling
New hardware/drivers:
- mobile:
- iosm: PCIe Driver for Intel M.2 Modem
- support for Qualcomm MSM8998 (ipa)
- WiFi: Qualcomm QCN9074 and WCN6855 PCI devices
- sparx5: Microchip SparX-5 family of Enterprise Ethernet switches
- Mellanox BlueField Gigabit Ethernet (control NIC of the DPU)
- NXP SJA1110 Automotive Ethernet 10-port switch
- Qualcomm QCA8327 switch support (qca8k)
- Mikrotik 10/25G NIC (atl1c)
Driver changes:
- ACPI support for some MDIO, MAC and PHY devices from Marvell and
NXP (our first foray into MAC/PHY description via ACPI)
- HW timestamping (PTP) support: bnxt_en, ice, sja1105, hns3, tja11xx
- Mellanox/Nvidia NIC (mlx5)
- NIC VF offload of L2 bridging
- support IRQ distribution to Sub-functions
- Marvell (prestera):
- add flower and match all
- devlink trap
- link aggregation
- Netronome (nfp): connection tracking offload
- Intel 1GE (igc): add AF_XDP support
- Marvell DPU (octeontx2): ingress ratelimit offload
- Google vNIC (gve): new ring/descriptor format support
- Qualcomm mobile (rmnet & ipa): inline checksum offload support
- MediaTek WiFi (mt76)
- mt7915 MSI support
- mt7915 Tx status reporting
- mt7915 thermal sensors support
- mt7921 decapsulation offload
- mt7921 enable runtime pm and deep sleep
- Realtek WiFi (rtw88)
- beacon filter support
- Tx antenna path diversity support
- firmware crash information via devcoredump
- Qualcomm WiFi (wcn36xx)
- Wake-on-WLAN support with magic packets and GTK rekeying
- Micrel PHY (ksz886x/ksz8081): add cable test support"
* tag 'net-next-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2168 commits)
tcp: change ICSK_CA_PRIV_SIZE definition
tcp_yeah: check struct yeah size at compile time
gve: DQO: Fix off by one in gve_rx_dqo()
stmmac: intel: set PCI_D3hot in suspend
stmmac: intel: Enable PHY WOL option in EHL
net: stmmac: option to enable PHY WOL with PMT enabled
net: say "local" instead of "static" addresses in ndo_dflt_fdb_{add,del}
net: use netdev_info in ndo_dflt_fdb_{add,del}
ptp: Set lookup cookie when creating a PTP PPS source.
net: sock: add trace for socket errors
net: sock: introduce sk_error_report
net: dsa: replay the local bridge FDB entries pointing to the bridge dev too
net: dsa: ensure during dsa_fdb_offload_notify that dev_hold and dev_put are on the same dev
net: dsa: include fdb entries pointing to bridge in the host fdb list
net: dsa: include bridge addresses which are local in the host fdb list
net: dsa: sync static FDB entries on foreign interfaces to hardware
net: dsa: install the host MDB and FDB entries in the master's RX filter
net: dsa: reference count the FDB addresses at the cross-chip notifier level
net: dsa: introduce a separate cross-chip notifier type for host FDBs
net: dsa: reference count the MDB entries at the cross-chip notifier level
...
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/netiucv.c | 28 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 42 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 349 | ||||
-rw-r--r-- | drivers/s390/net/qeth_ethtool.c | 7 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 12 |
5 files changed, 190 insertions, 248 deletions
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 260860cf3aa1..5a0c2f07a3a2 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -118,24 +118,6 @@ static struct device_driver netiucv_driver = { .bus = &iucv_bus, }; -static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *); -static void netiucv_callback_connack(struct iucv_path *, u8 *); -static void netiucv_callback_connrej(struct iucv_path *, u8 *); -static void netiucv_callback_connsusp(struct iucv_path *, u8 *); -static void netiucv_callback_connres(struct iucv_path *, u8 *); -static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *); -static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *); - -static struct iucv_handler netiucv_handler = { - .path_pending = netiucv_callback_connreq, - .path_complete = netiucv_callback_connack, - .path_severed = netiucv_callback_connrej, - .path_quiesced = netiucv_callback_connsusp, - .path_resumed = netiucv_callback_connres, - .message_pending = netiucv_callback_rx, - .message_complete = netiucv_callback_txdone -}; - /** * Per connection profiling data */ @@ -774,6 +756,16 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) } } +static struct iucv_handler netiucv_handler = { + .path_pending = netiucv_callback_connreq, + .path_complete = netiucv_callback_connack, + .path_severed = netiucv_callback_connrej, + .path_quiesced = netiucv_callback_connsusp, + .path_resumed = netiucv_callback_connres, + .message_pending = netiucv_callback_rx, + .message_complete = netiucv_callback_txdone, +}; + static void conn_action_connaccept(fsm_instance *fi, int event, void *arg) { struct iucv_event *ev = arg; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index fd9b869d278e..f4d554ea0c93 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -417,13 +417,17 @@ enum qeth_qdio_out_buffer_state { QETH_QDIO_BUF_EMPTY, /* Filled by driver; owned by hardware in order to be sent. */ QETH_QDIO_BUF_PRIMED, - /* Discovered by the TX completion code: */ - QETH_QDIO_BUF_PENDING, - /* Finished by the TX completion code: */ - QETH_QDIO_BUF_NEED_QAOB, - /* Received QAOB notification on CQ: */ - QETH_QDIO_BUF_QAOB_OK, - QETH_QDIO_BUF_QAOB_ERROR, +}; + +enum qeth_qaob_state { + QETH_QAOB_ISSUED, + QETH_QAOB_PENDING, + QETH_QAOB_DONE, +}; + +struct qeth_qaob_priv1 { + unsigned int state; + u8 queue_no; }; struct qeth_qdio_out_buffer { @@ -433,9 +437,8 @@ struct qeth_qdio_out_buffer { unsigned int frames; unsigned int bytes; struct sk_buff_head skb_list; - int is_header[QDIO_MAX_ELEMENTS_PER_BUFFER]; + DECLARE_BITMAP(from_kmem_cache, QDIO_MAX_ELEMENTS_PER_BUFFER); - struct qeth_qdio_out_q *q; struct list_head list_entry; struct qaob *aob; }; @@ -483,6 +486,7 @@ struct qeth_out_q_stats { u64 stopped; u64 doorbell; u64 coal_frames; + u64 completion_irq; u64 completion_yield; u64 completion_timer; @@ -526,6 +530,7 @@ struct qeth_qdio_out_q { unsigned int coalesce_usecs; unsigned int max_coalesced_frames; + unsigned int rescan_usecs; }; #define qeth_for_each_output_queue(card, q, i) \ @@ -612,7 +617,6 @@ struct qeth_channel { struct ccw_device *ccwdev; struct qeth_cmd_buffer *active_cmd; enum qeth_channel_states state; - atomic_t irq_pending; }; struct qeth_reply { @@ -662,11 +666,6 @@ static inline struct ccw1 *__ccw_from_cmd(struct qeth_cmd_buffer *iob) return (struct ccw1 *)(iob->data + ALIGN(iob->length, 8)); } -static inline bool qeth_trylock_channel(struct qeth_channel *channel) -{ - return atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0; -} - /** * OSA card related definitions */ @@ -886,13 +885,24 @@ static inline bool qeth_card_hw_is_reachable(struct qeth_card *card) return card->state == CARD_STATE_SOFTSETUP; } +static inline bool qeth_use_tx_irqs(struct qeth_card *card) +{ + return !IS_IQD(card); +} + static inline void qeth_unlock_channel(struct qeth_card *card, struct qeth_channel *channel) { - atomic_set(&channel->irq_pending, 0); + xchg(&channel->active_cmd, NULL); wake_up(&card->wait_q); } +static inline bool qeth_trylock_channel(struct qeth_channel *channel, + struct qeth_cmd_buffer *cmd) +{ + return cmpxchg(&channel->active_cmd, NULL, cmd) == NULL; +} + struct qeth_trap_id { __u16 lparnr; char vmname[8]; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index a1f08e9aa064..62f88ccbd03f 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -70,9 +70,6 @@ static void qeth_issue_next_read_cb(struct qeth_card *card, unsigned int data_length); static int qeth_qdio_establish(struct qeth_card *); static void qeth_free_qdio_queues(struct qeth_card *card); -static void qeth_notify_skbs(struct qeth_qdio_out_q *queue, - struct qeth_qdio_out_buffer *buf, - enum iucv_tx_notify notification); static void qeth_close_dev_handler(struct work_struct *work) { @@ -434,65 +431,6 @@ static enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, return n; } -static void qeth_qdio_handle_aob(struct qeth_card *card, - unsigned long phys_aob_addr) -{ - enum qeth_qdio_out_buffer_state new_state = QETH_QDIO_BUF_QAOB_OK; - struct qaob *aob; - struct qeth_qdio_out_buffer *buffer; - enum iucv_tx_notify notification; - struct qeth_qdio_out_q *queue; - unsigned int i; - - aob = (struct qaob *) phys_to_virt(phys_aob_addr); - QETH_CARD_TEXT(card, 5, "haob"); - QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr); - buffer = (struct qeth_qdio_out_buffer *) aob->user1; - QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); - - 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, let it handle the async - * completion for us. It will also recycle the QAOB. - */ - break; - case QETH_QDIO_BUF_PENDING: - /* TX completion code is active and will handle the async - * completion for us. It will also recycle the QAOB. - */ - 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); - - /* Free dangling allocations. The attached skbs are handled by - * qeth_tx_complete_pending_bufs(), and so is the QAOB. - */ - for (i = 0; - i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card); - i++) { - void *data = phys_to_virt(aob->sba[i]); - - if (data && buffer->is_header[i]) - kmem_cache_free(qeth_core_header_cache, data); - buffer->is_header[i] = 0; - } - - queue = buffer->q; - atomic_set(&buffer->state, QETH_QDIO_BUF_EMPTY); - napi_schedule(&queue->napi); - break; - default: - WARN_ON_ONCE(1); - } -} - static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u8 flags, u32 len, void *data) { @@ -1268,7 +1206,6 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, iob = (struct qeth_cmd_buffer *) (addr_t)intparm; } - channel->active_cmd = NULL; qeth_unlock_channel(card, channel); rc = qeth_check_irb_error(card, cdev, irb); @@ -1353,10 +1290,10 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q, } } -static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error, +static void qeth_tx_complete_buf(struct qeth_qdio_out_q *queue, + struct qeth_qdio_out_buffer *buf, bool error, int budget) { - struct qeth_qdio_out_q *queue = buf->q; struct sk_buff *skb; /* Empty buffer? */ @@ -1400,17 +1337,18 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, int i; /* is PCI flag set on buffer? */ - if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) + if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) { atomic_dec(&queue->set_pci_flags_count); + QETH_TXQ_STAT_INC(queue, completion_irq); + } - qeth_tx_complete_buf(buf, error, budget); + qeth_tx_complete_buf(queue, buf, error, budget); for (i = 0; i < queue->max_elements; ++i) { void *data = phys_to_virt(buf->buffer->element[i].addr); - if (data && buf->is_header[i]) + if (__test_and_clear_bit(i, buf->from_kmem_cache) && data) kmem_cache_free(qeth_core_header_cache, data); - buf->is_header[i] = 0; } qeth_scrub_qdio_buffer(buf->buffer, queue->max_elements); @@ -1434,14 +1372,30 @@ static void qeth_tx_complete_pending_bufs(struct qeth_card *card, struct qeth_qdio_out_buffer *buf, *tmp; list_for_each_entry_safe(buf, tmp, &queue->pending_bufs, list_entry) { - if (drain || atomic_read(&buf->state) == QETH_QDIO_BUF_EMPTY) { + struct qeth_qaob_priv1 *priv; + struct qaob *aob = buf->aob; + enum iucv_tx_notify notify; + unsigned int i; + + priv = (struct qeth_qaob_priv1 *)&aob->user1; + if (drain || READ_ONCE(priv->state) == QETH_QAOB_DONE) { QETH_CARD_TEXT(card, 5, "fp"); QETH_CARD_TEXT_(card, 5, "%lx", (long) buf); - if (drain) - qeth_notify_skbs(queue, buf, - TX_NOTIFY_GENERALERROR); - qeth_tx_complete_buf(buf, drain, budget); + notify = drain ? TX_NOTIFY_GENERALERROR : + qeth_compute_cq_notification(aob->aorc, 1); + qeth_notify_skbs(queue, buf, notify); + qeth_tx_complete_buf(queue, buf, drain, budget); + + for (i = 0; + i < aob->sb_count && i < queue->max_elements; + i++) { + void *data = phys_to_virt(aob->sba[i]); + + if (test_bit(i, buf->from_kmem_cache) && data) + kmem_cache_free(qeth_core_header_cache, + data); + } list_del(&buf->list_entry); qeth_free_out_buf(buf); @@ -1713,11 +1667,10 @@ static int qeth_stop_channel(struct qeth_channel *channel) rc = ccw_device_set_offline(cdev); spin_lock_irq(get_ccwdev_lock(cdev)); - if (channel->active_cmd) { + if (channel->active_cmd) dev_err(&cdev->dev, "Stopped channel while cmd %px was still active\n", channel->active_cmd); - channel->active_cmd = NULL; - } + cdev->handler = NULL; spin_unlock_irq(get_ccwdev_lock(cdev)); @@ -1730,7 +1683,7 @@ static int qeth_start_channel(struct qeth_channel *channel) int rc; channel->state = CH_STATE_DOWN; - atomic_set(&channel->irq_pending, 0); + xchg(&channel->active_cmd, NULL); spin_lock_irq(get_ccwdev_lock(cdev)); cdev->handler = qeth_irq; @@ -2037,7 +1990,7 @@ static int qeth_send_control_data(struct qeth_card *card, reply->param = reply_param; timeout = wait_event_interruptible_timeout(card->wait_q, - qeth_trylock_channel(channel), + qeth_trylock_channel(channel, iob), timeout); if (timeout <= 0) { qeth_put_cmd(iob); @@ -2057,8 +2010,6 @@ static int qeth_send_control_data(struct qeth_card *card, spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob), (addr_t) iob, 0, 0, timeout); - if (!rc) - channel->active_cmd = iob; spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) { QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n", @@ -2578,7 +2529,6 @@ static int qeth_alloc_out_buf(struct qeth_qdio_out_q *q, unsigned int bidx, newbuf->buffer = q->qdio_bufs[bidx]; skb_queue_head_init(&newbuf->skb_list); lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); - newbuf->q = q; atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); q->bufs[bidx] = newbuf; return 0; @@ -2663,8 +2613,15 @@ static int qeth_alloc_qdio_queues(struct qeth_card *card) INIT_LIST_HEAD(&queue->pending_bufs); spin_lock_init(&queue->lock); timer_setup(&queue->timer, qeth_tx_completion_timer, 0); - queue->coalesce_usecs = QETH_TX_COALESCE_USECS; - queue->max_coalesced_frames = QETH_TX_MAX_COALESCED_FRAMES; + if (IS_IQD(card)) { + queue->coalesce_usecs = QETH_TX_COALESCE_USECS; + queue->max_coalesced_frames = QETH_TX_MAX_COALESCED_FRAMES; + queue->rescan_usecs = QETH_TX_TIMER_USECS; + } else { + queue->coalesce_usecs = USEC_PER_SEC; + queue->max_coalesced_frames = 0; + queue->rescan_usecs = 10 * USEC_PER_SEC; + } queue->priority = QETH_QIB_PQUE_PRIO_DEFAULT; } @@ -3601,8 +3558,8 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, int count) { struct qeth_qdio_out_buffer *buf = queue->bufs[index]; - unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT; struct qeth_card *card = queue->card; + unsigned int frames, usecs; struct qaob *aob = NULL; int rc; int i; @@ -3629,8 +3586,12 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, if (!buf->aob) buf->aob = qdio_allocate_aob(); if (buf->aob) { + struct qeth_qaob_priv1 *priv; + aob = buf->aob; - aob->user1 = (u64) buf; + priv = (struct qeth_qaob_priv1 *)&aob->user1; + priv->state = QETH_QAOB_ISSUED; + priv->queue_no = queue->queue_no; } } } else { @@ -3658,14 +3619,11 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; } } - - if (atomic_read(&queue->set_pci_flags_count)) - qdio_flags |= QDIO_FLAG_PCI_OUT; } QETH_TXQ_STAT_INC(queue, doorbell); - rc = do_QDIO(CARD_DDEV(card), qdio_flags, queue->queue_no, index, count, - aob); + rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_OUTPUT, queue->queue_no, + index, count, aob); switch (rc) { case 0: @@ -3673,17 +3631,20 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, /* ignore temporary SIGA errors without busy condition */ /* Fake the TX completion interrupt: */ - if (IS_IQD(card)) { - unsigned int frames = READ_ONCE(queue->max_coalesced_frames); - unsigned int usecs = READ_ONCE(queue->coalesce_usecs); + frames = READ_ONCE(queue->max_coalesced_frames); + usecs = READ_ONCE(queue->coalesce_usecs); - if (frames && queue->coalesced_frames >= frames) { - napi_schedule(&queue->napi); - queue->coalesced_frames = 0; - QETH_TXQ_STAT_INC(queue, coal_frames); - } else if (usecs) { - qeth_tx_arm_timer(queue, usecs); - } + if (frames && queue->coalesced_frames >= frames) { + napi_schedule(&queue->napi); + queue->coalesced_frames = 0; + QETH_TXQ_STAT_INC(queue, coal_frames); + } else if (qeth_use_tx_irqs(card) && + atomic_read(&queue->used_buffers) >= 32) { + /* Old behaviour carried over from the qdio layer: */ + napi_schedule(&queue->napi); + QETH_TXQ_STAT_INC(queue, coal_frames); + } else if (usecs) { + qeth_tx_arm_timer(queue, usecs); } break; @@ -3769,6 +3730,18 @@ out: } EXPORT_SYMBOL_GPL(qeth_configure_cq); +static void qeth_qdio_handle_aob(struct qeth_card *card, struct qaob *aob) +{ + struct qeth_qaob_priv1 *priv = (struct qeth_qaob_priv1 *)&aob->user1; + unsigned int queue_no = priv->queue_no; + + BUILD_BUG_ON(sizeof(*priv) > ARRAY_SIZE(aob->user1)); + + if (xchg(&priv->state, QETH_QAOB_DONE) == QETH_QAOB_PENDING && + queue_no < card->qdio.no_out_queues) + napi_schedule(&card->qdio.out_qs[queue_no]->napi); +} + static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, unsigned int queue, int first_element, int count) @@ -3795,7 +3768,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, buffer->element[e].addr) { unsigned long phys_aob_addr = buffer->element[e].addr; - qeth_qdio_handle_aob(card, phys_aob_addr); + qeth_qdio_handle_aob(card, phys_to_virt(phys_aob_addr)); ++e; } qeth_scrub_qdio_buffer(buffer, QDIO_MAX_ELEMENTS_PER_BUFFER); @@ -3831,36 +3804,14 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, unsigned long card_ptr) { struct qeth_card *card = (struct qeth_card *) card_ptr; - struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; struct net_device *dev = card->dev; - struct netdev_queue *txq; - int i; QETH_CARD_TEXT(card, 6, "qdouhdl"); if (qdio_error & QDIO_ERROR_FATAL) { QETH_CARD_TEXT(card, 2, "achkcond"); netif_tx_stop_all_queues(dev); qeth_schedule_recovery(card); - return; } - - for (i = first_element; i < (first_element + count); ++i) { - 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); - qeth_check_outbound_queue(queue); - - txq = netdev_get_tx_queue(dev, __queue); - /* xmit may have observed the full-condition, but not yet stopped the - * txq. In which case the code below won't trigger. So before returning, - * xmit will re-check the txq's fill level and wake it up if needed. - */ - if (netif_tx_queue_stopped(txq) && !qeth_out_queue_is_full(queue)) - netif_tx_wake_queue(txq); } /** @@ -4101,7 +4052,7 @@ static unsigned int qeth_fill_buffer(struct qeth_qdio_out_buffer *buf, /* HW header is allocated from cache: */ if ((void *)hdr != skb->data) - buf->is_header[element] = 1; + __set_bit(element, buf->from_kmem_cache); /* HW header was pushed and is contiguous with linear part: */ else if (length > 0 && !PAGE_ALIGNED(data) && (data == (char *)hdr + hd_len)) @@ -5256,7 +5207,6 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = in_sbal_ptrs; init_data.output_sbal_addr_array = out_sbal_ptrs; - init_data.scan_threshold = IS_IQD(card) ? 0 : 32; if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { @@ -5956,9 +5906,10 @@ static unsigned int qeth_rx_poll(struct qeth_card *card, int budget) /* Fetch completed RX buffers: */ if (!card->rx.b_count) { card->rx.qdio_err = 0; - card->rx.b_count = qdio_get_next_buffers( - card->data.ccwdev, 0, &card->rx.b_index, - &card->rx.qdio_err); + card->rx.b_count = qdio_inspect_queue(CARD_DDEV(card), + 0, true, + &card->rx.b_index, + &card->rx.qdio_err); if (card->rx.b_count <= 0) { card->rx.b_count = 0; break; @@ -6022,6 +5973,16 @@ int qeth_poll(struct napi_struct *napi, int budget) work_done = qeth_rx_poll(card, budget); + if (qeth_use_tx_irqs(card)) { + struct qeth_qdio_out_q *queue; + unsigned int i; + + qeth_for_each_output_queue(card, queue, i) { + if (!qeth_out_queue_is_empty(queue)) + napi_schedule(&queue->napi); + } + } + if (card->options.cq == QETH_CQ_ENABLED) qeth_cq_poll(card); @@ -6055,6 +6016,8 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, if (qdio_error == QDIO_ERROR_SLSB_PENDING) { struct qaob *aob = buffer->aob; + struct qeth_qaob_priv1 *priv; + enum iucv_tx_notify notify; if (!aob) { netdev_WARN_ONCE(card->dev, @@ -6066,60 +6029,27 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, QETH_CARD_TEXT_(card, 5, "pel%u", bidx); - switch (atomic_cmpxchg(&buffer->state, - QETH_QDIO_BUF_PRIMED, - QETH_QDIO_BUF_PENDING)) { - case QETH_QDIO_BUF_PRIMED: - /* We have initial ownership, no QAOB (yet): */ + priv = (struct qeth_qaob_priv1 *)&aob->user1; + /* QAOB hasn't completed yet: */ + if (xchg(&priv->state, QETH_QAOB_PENDING) != QETH_QAOB_DONE) { 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. */ - - /* Prepare the queue slot for immediate re-use: */ - qeth_scrub_qdio_buffer(buffer->buffer, queue->max_elements); - if (qeth_alloc_out_buf(queue, bidx, - GFP_ATOMIC)) { - QETH_CARD_TEXT(card, 2, "outofbuf"); - qeth_schedule_recovery(card); - } - - list_add(&buffer->list_entry, - &queue->pending_bufs); - /* Skip clearing the buffer: */ - return; - case QETH_QDIO_BUF_QAOB_OK: - qeth_notify_skbs(queue, buffer, - TX_NOTIFY_DELAYED_OK); - error = false; - break; - case QETH_QDIO_BUF_QAOB_ERROR: - qeth_notify_skbs(queue, buffer, - TX_NOTIFY_DELAYED_GENERALERROR); - error = true; - break; - default: - WARN_ON_ONCE(1); + /* Prepare the queue slot for immediate re-use: */ + qeth_scrub_qdio_buffer(buffer->buffer, queue->max_elements); + if (qeth_alloc_out_buf(queue, bidx, GFP_ATOMIC)) { + QETH_CARD_TEXT(card, 2, "outofbuf"); + qeth_schedule_recovery(card); } - break; - case QETH_QDIO_BUF_QAOB_OK: - /* qeth_qdio_handle_aob() already received a QAOB: */ - qeth_notify_skbs(queue, buffer, TX_NOTIFY_OK); - error = false; - break; - case QETH_QDIO_BUF_QAOB_ERROR: - /* qeth_qdio_handle_aob() already received a QAOB: */ - qeth_notify_skbs(queue, buffer, TX_NOTIFY_GENERALERROR); - error = true; - break; - default: - WARN_ON_ONCE(1); + list_add(&buffer->list_entry, &queue->pending_bufs); + /* Skip clearing the buffer: */ + return; } + /* QAOB already completed: */ + notify = qeth_compute_cq_notification(aob->aorc, 0); + qeth_notify_skbs(queue, buffer, notify); + error = !!aob->aorc; memset(aob, 0, sizeof(*aob)); } else if (card->options.cq == QETH_CQ_ENABLED) { qeth_notify_skbs(queue, buffer, @@ -6138,7 +6068,10 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget) unsigned int work_done = 0; struct netdev_queue *txq; - txq = netdev_get_tx_queue(dev, qeth_iqd_translate_txq(dev, queue_no)); + if (IS_IQD(card)) + txq = netdev_get_tx_queue(dev, qeth_iqd_translate_txq(dev, queue_no)); + else + txq = netdev_get_tx_queue(dev, queue_no); while (1) { unsigned int start, error, i; @@ -6165,8 +6098,9 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget) &start, &error); if (completed <= 0) { /* Ensure we see TX completion for pending work: */ - if (napi_complete_done(napi, 0)) - qeth_tx_arm_timer(queue, QETH_TX_TIMER_USECS); + if (napi_complete_done(napi, 0) && + !atomic_read(&queue->set_pci_flags_count)) + qeth_tx_arm_timer(queue, queue->rescan_usecs); return 0; } @@ -6179,12 +6113,19 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget) bytes += buffer->bytes; qeth_handle_send_error(card, buffer, error); - qeth_iqd_tx_complete(queue, bidx, error, budget); + if (IS_IQD(card)) + qeth_iqd_tx_complete(queue, bidx, error, budget); + else + qeth_clear_output_buffer(queue, buffer, error, + budget); } - netdev_tx_completed_queue(txq, packets, bytes); atomic_sub(completed, &queue->used_buffers); work_done += completed; + if (IS_IQD(card)) + netdev_tx_completed_queue(txq, packets, bytes); + else + qeth_check_outbound_queue(queue); /* xmit may have observed the full-condition, but not yet * stopped the txq. In which case the code below won't trigger. @@ -7228,6 +7169,8 @@ EXPORT_SYMBOL_GPL(qeth_iqd_select_queue); int qeth_open(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; + struct qeth_qdio_out_q *queue; + unsigned int i; QETH_CARD_TEXT(card, 4, "qethopen"); @@ -7235,16 +7178,11 @@ int qeth_open(struct net_device *dev) netif_tx_start_all_queues(dev); local_bh_disable(); - if (IS_IQD(card)) { - struct qeth_qdio_out_q *queue; - unsigned int i; - - qeth_for_each_output_queue(card, queue, i) { - netif_tx_napi_add(dev, &queue->napi, qeth_tx_poll, - QETH_NAPI_WEIGHT); - napi_enable(&queue->napi); - napi_schedule(&queue->napi); - } + qeth_for_each_output_queue(card, queue, i) { + netif_tx_napi_add(dev, &queue->napi, qeth_tx_poll, + QETH_NAPI_WEIGHT); + napi_enable(&queue->napi); + napi_schedule(&queue->napi); } napi_enable(&card->napi); @@ -7259,6 +7197,8 @@ EXPORT_SYMBOL_GPL(qeth_open); int qeth_stop(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; + struct qeth_qdio_out_q *queue; + unsigned int i; QETH_CARD_TEXT(card, 4, "qethstop"); @@ -7266,24 +7206,17 @@ int qeth_stop(struct net_device *dev) cancel_delayed_work_sync(&card->buffer_reclaim_work); qdio_stop_irq(CARD_DDEV(card)); - if (IS_IQD(card)) { - struct qeth_qdio_out_q *queue; - unsigned int i; - - /* Quiesce the NAPI instances: */ - qeth_for_each_output_queue(card, queue, i) - napi_disable(&queue->napi); + /* Quiesce the NAPI instances: */ + qeth_for_each_output_queue(card, queue, i) + napi_disable(&queue->napi); - /* Stop .ndo_start_xmit, might still access queue->napi. */ - netif_tx_disable(dev); + /* Stop .ndo_start_xmit, might still access queue->napi. */ + netif_tx_disable(dev); - qeth_for_each_output_queue(card, queue, i) { - del_timer_sync(&queue->timer); - /* Queues may get re-allocated, so remove the NAPIs. */ - netif_napi_del(&queue->napi); - } - } else { - netif_tx_disable(dev); + qeth_for_each_output_queue(card, queue, i) { + del_timer_sync(&queue->timer); + /* Queues may get re-allocated, so remove the NAPIs. */ + netif_napi_del(&queue->napi); } return 0; diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index 3a51bbff0ffe..2c4cb300a8fc 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -41,6 +41,7 @@ static const struct qeth_stats txq_stats[] = { QETH_TXQ_STAT("Queue stopped", stopped), QETH_TXQ_STAT("Doorbell", doorbell), QETH_TXQ_STAT("IRQ for frames", coal_frames), + QETH_TXQ_STAT("Completion IRQ", completion_irq), QETH_TXQ_STAT("Completion yield", completion_yield), QETH_TXQ_STAT("Completion timer", completion_timer), }; @@ -79,10 +80,8 @@ static void qeth_add_stat_strings(u8 **data, const char *prefix, { unsigned int i; - for (i = 0; i < size; i++) { - snprintf(*data, ETH_GSTRING_LEN, "%s%s", prefix, stats[i].name); - *data += ETH_GSTRING_LEN; - } + for (i = 0; i < size; i++) + ethtool_sprintf(data, "%s%s", prefix, stats[i].name); } static int qeth_get_sset_count(struct net_device *dev, int stringset) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ca44421a6d6e..2abf86c104d5 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -805,8 +805,6 @@ static int qeth_l2_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, if (!netif_device_present(dev)) return -ENODEV; - if (!(priv->brport_hw_features)) - return -EOPNOTSUPP; nlmsg_for_each_attr(attr, nlh, sizeof(struct ifinfomsg), rem1) { if (nla_type(attr) == IFLA_PROTINFO) { @@ -832,6 +830,16 @@ static int qeth_l2_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, return 0; if (!bp_tb[IFLA_BRPORT_LEARNING_SYNC]) return -EINVAL; + if (!(priv->brport_hw_features & BR_LEARNING_SYNC)) { + NL_SET_ERR_MSG_ATTR(extack, bp_tb[IFLA_BRPORT_LEARNING_SYNC], + "Operation not supported by HW"); + return -EOPNOTSUPP; + } + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) { + NL_SET_ERR_MSG_ATTR(extack, bp_tb[IFLA_BRPORT_LEARNING_SYNC], + "Requires NET_SWITCHDEV"); + return -EOPNOTSUPP; + } enable = !!nla_get_u8(bp_tb[IFLA_BRPORT_LEARNING_SYNC]); if (enable == !!(priv->brport_features & BR_LEARNING_SYNC)) |