summaryrefslogtreecommitdiff
path: root/drivers/s390/net/qeth_l3_main.c
diff options
context:
space:
mode:
authorFrank Blaschka <frank.blaschka@de.ibm.com>2010-09-07 21:14:42 +0000
committerDavid S. Miller <davem@davemloft.net>2010-09-08 14:31:02 -0700
commita1c3ed4c9ca01dded8d511a1d1daf271fbae8d89 (patch)
tree1ff0b2887e550f127fe844222b6833b831a4b036 /drivers/s390/net/qeth_l3_main.c
parent81d53749416995538f830c8e4d3fbaf1769b9375 (diff)
qeth: NAPI support for l2 and l3 discipline
This patch adds NAPI support to the qeth layer 2 and layer 3 discipline. It is important to understand that we can not enable/disable IRQs as usual, we have to use the corresponding new QDIO interface. Also to not overdraw the budget we have to stop and restart buffer processing at any point during processing a bulk of QDIO buffers. Having the driver NAPI enabled it is possible to turn on GRO for the layer 3 discipline. Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/net/qeth_l3_main.c')
-rw-r--r--drivers/s390/net/qeth_l3_main.c182
1 files changed, 111 insertions, 71 deletions
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 561bdc881e92..5b79f573bd93 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2107,51 +2107,44 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card,
return vlan_id;
}
-static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
- struct qeth_qdio_buffer *buf, int index)
+static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
+ int budget, int *done)
{
- struct qdio_buffer_element *element;
+ int work_done = 0;
struct sk_buff *skb;
struct qeth_hdr *hdr;
- int offset;
__u16 vlan_tag = 0;
unsigned int len;
- /* get first element of current buffer */
- element = (struct qdio_buffer_element *)&buf->buffer->element[0];
- offset = 0;
- if (card->options.performance_stats)
- card->perf_stats.bufs_rec++;
- while ((skb = qeth_core_get_next_skb(card, buf->buffer, &element,
- &offset, &hdr))) {
- skb->dev = card->dev;
- /* is device UP ? */
- if (!(card->dev->flags & IFF_UP)) {
- dev_kfree_skb_any(skb);
- continue;
- }
+ *done = 0;
+ BUG_ON(!budget);
+ while (budget) {
+ skb = qeth_core_get_next_skb(card,
+ card->qdio.in_q->bufs[card->rx.b_index].buffer,
+ &card->rx.b_element, &card->rx.e_offset, &hdr);
+ if (!skb) {
+ *done = 1;
+ break;
+ }
+ skb->dev = card->dev;
switch (hdr->hdr.l3.id) {
case QETH_HEADER_TYPE_LAYER3:
vlan_tag = qeth_l3_rebuild_skb(card, skb, hdr);
len = skb->len;
if (vlan_tag && !card->options.sniffer)
if (card->vlangrp)
- vlan_hwaccel_rx(skb, card->vlangrp,
- vlan_tag);
+ vlan_gro_receive(&card->napi,
+ card->vlangrp, vlan_tag, skb);
else {
dev_kfree_skb_any(skb);
continue;
}
else
- netif_rx(skb);
+ napi_gro_receive(&card->napi, skb);
break;
case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- if (card->options.checksum_type == NO_CHECKSUMMING)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
len = skb->len;
netif_receive_skb(skb);
break;
@@ -2161,10 +2154,87 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
continue;
}
-
+ work_done++;
+ budget--;
card->stats.rx_packets++;
card->stats.rx_bytes += len;
}
+ return work_done;
+}
+
+static int qeth_l3_poll(struct napi_struct *napi, int budget)
+{
+ struct qeth_card *card = container_of(napi, struct qeth_card, napi);
+ int work_done = 0;
+ struct qeth_qdio_buffer *buffer;
+ int done;
+ int new_budget = budget;
+
+ if (card->options.performance_stats) {
+ card->perf_stats.inbound_cnt++;
+ card->perf_stats.inbound_start_time = qeth_get_micros();
+ }
+
+ while (1) {
+ 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);
+ if (card->rx.b_count <= 0) {
+ card->rx.b_count = 0;
+ break;
+ }
+ card->rx.b_element =
+ &card->qdio.in_q->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+
+ while (card->rx.b_count) {
+ buffer = &card->qdio.in_q->bufs[card->rx.b_index];
+ if (!(card->rx.qdio_err &&
+ qeth_check_qdio_errors(card, buffer->buffer,
+ card->rx.qdio_err, "qinerr")))
+ work_done += qeth_l3_process_inbound_buffer(
+ card, new_budget, &done);
+ else
+ done = 1;
+
+ if (done) {
+ if (card->options.performance_stats)
+ card->perf_stats.bufs_rec++;
+ qeth_put_buffer_pool_entry(card,
+ buffer->pool_entry);
+ qeth_queue_input_buffer(card, card->rx.b_index);
+ card->rx.b_count--;
+ if (card->rx.b_count) {
+ card->rx.b_index =
+ (card->rx.b_index + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ card->rx.b_element =
+ &card->qdio.in_q
+ ->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+ }
+
+ if (work_done >= budget)
+ goto out;
+ else
+ new_budget = budget - work_done;
+ }
+ }
+
+ napi_complete(napi);
+ if (qdio_start_irq(card->data.ccwdev, 0))
+ napi_schedule(&card->napi);
+out:
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
+ return work_done;
}
static int qeth_l3_verify_vlan_dev(struct net_device *dev,
@@ -3098,6 +3168,7 @@ tx_drop:
static int qeth_l3_open(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
+ int rc = 0;
QETH_CARD_TEXT(card, 4, "qethopen");
if (card->state != CARD_STATE_SOFTSETUP)
@@ -3108,7 +3179,12 @@ static int qeth_l3_open(struct net_device *dev)
if (!card->lan_online && netif_carrier_ok(dev))
netif_carrier_off(dev);
- return 0;
+ if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
+ napi_enable(&card->napi);
+ napi_schedule(&card->napi);
+ } else
+ rc = -EIO;
+ return rc;
}
static int qeth_l3_stop(struct net_device *dev)
@@ -3117,8 +3193,10 @@ static int qeth_l3_stop(struct net_device *dev)
QETH_CARD_TEXT(card, 4, "qethstop");
netif_tx_disable(dev);
- if (card->state == CARD_STATE_UP)
+ if (card->state == CARD_STATE_UP) {
card->state = CARD_STATE_SOFTSETUP;
+ napi_disable(&card->napi);
+ }
return 0;
}
@@ -3288,57 +3366,19 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
card->dev->gso_max_size = 15 * PAGE_SIZE;
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
+ netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT);
return register_netdev(card->dev);
}
-static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
- unsigned int qdio_err, unsigned int queue, int first_element,
- int count, unsigned long card_ptr)
-{
- struct net_device *net_dev;
- struct qeth_card *card;
- struct qeth_qdio_buffer *buffer;
- int index;
- int i;
-
- card = (struct qeth_card *) card_ptr;
- net_dev = card->dev;
- if (card->options.performance_stats) {
- card->perf_stats.inbound_cnt++;
- card->perf_stats.inbound_start_time = qeth_get_micros();
- }
- if (qdio_err & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
- QETH_CARD_TEXT(card, 1, "qdinchk");
- QETH_CARD_TEXT_(card, 1, "%04X%04X",
- first_element, count);
- QETH_CARD_TEXT_(card, 1, "%04X", queue);
- qeth_schedule_recovery(card);
- return;
- }
- for (i = first_element; i < (first_element + count); ++i) {
- index = i % QDIO_MAX_BUFFERS_PER_Q;
- buffer = &card->qdio.in_q->bufs[index];
- if (!(qdio_err &&
- qeth_check_qdio_errors(card, buffer->buffer,
- qdio_err, "qinerr")))
- qeth_l3_process_inbound_buffer(card, buffer, index);
- /* clear buffer and give back to hardware */
- qeth_put_buffer_pool_entry(card, buffer->pool_entry);
- qeth_queue_input_buffer(card, index);
- }
- if (card->options.performance_stats)
- card->perf_stats.inbound_time += qeth_get_micros() -
- card->perf_stats.inbound_start_time;
-}
-
static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
qeth_l3_create_device_attributes(&gdev->dev);
card->options.layer2 = 0;
+ card->discipline.start_poll = qeth_qdio_start_poll;
card->discipline.input_handler = (qdio_handler_t *)
- qeth_l3_qdio_input_handler;
+ qeth_qdio_input_handler;
card->discipline.output_handler = (qdio_handler_t *)
qeth_qdio_output_handler;
card->discipline.recover = qeth_l3_recover;
@@ -3397,6 +3437,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
}
card->state = CARD_STATE_HARDSETUP;
+ memset(&card->rx, 0, sizeof(struct qeth_rx));
qeth_print_status_message(card);
/* softsetup */
@@ -3533,9 +3574,6 @@ static int qeth_l3_recover(void *ptr)
card->use_hard_stop = 1;
__qeth_l3_set_offline(card->gdev, 1);
rc = __qeth_l3_set_online(card->gdev, 1);
- /* don't run another scheduled recovery */
- qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
- qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
if (!rc)
dev_info(&card->gdev->dev,
"Device successfully recovered!\n");
@@ -3546,6 +3584,8 @@ static int qeth_l3_recover(void *ptr)
dev_warn(&card->gdev->dev, "The qeth device driver "
"failed to recover an error on the device\n");
}
+ qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
+ qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
return 0;
}