diff options
Diffstat (limited to 'drivers/s390/net/qeth_l2_main.c')
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 173 |
1 files changed, 108 insertions, 65 deletions
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 830d63524d61..01c3c1f77879 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -407,29 +407,25 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) return rc; } -static void qeth_l2_process_inbound_buffer(struct qeth_card *card, - struct qeth_qdio_buffer *buf, int index) +static int qeth_l2_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; 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.l2.id) { case QETH_HEADER_TYPE_LAYER2: skb->pkt_type = PACKET_HOST; @@ -441,7 +437,7 @@ static void qeth_l2_process_inbound_buffer(struct qeth_card *card, if (skb->protocol == htons(ETH_P_802_2)) *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; len = skb->len; - netif_rx(skb); + netif_receive_skb(skb); break; case QETH_HEADER_TYPE_OSN: if (card->info.type == QETH_CARD_TYPE_OSN) { @@ -459,9 +455,87 @@ static void qeth_l2_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_l2_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_l2_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_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, @@ -755,49 +829,10 @@ tx_drop: return NETDEV_TX_OK; } -static void qeth_l2_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_l2_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_l2_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) @@ -814,18 +849,24 @@ static int qeth_l2_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_l2_stop(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; 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; } @@ -836,8 +877,9 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) INIT_LIST_HEAD(&card->vid_list); INIT_LIST_HEAD(&card->mc_list); card->options.layer2 = 1; + card->discipline.start_poll = qeth_qdio_start_poll; card->discipline.input_handler = (qdio_handler_t *) - qeth_l2_qdio_input_handler; + qeth_qdio_input_handler; card->discipline.output_handler = (qdio_handler_t *) qeth_qdio_output_handler; card->discipline.recover = qeth_l2_recover; @@ -923,6 +965,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->info.broadcast_capable = 1; qeth_l2_request_initial_mac(card); SET_NETDEV_DEV(card->dev, &card->gdev->dev); + netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT); return register_netdev(card->dev); } @@ -955,6 +998,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); card->state = CARD_STATE_HARDSETUP; + memset(&card->rx, 0, sizeof(struct qeth_rx)); qeth_print_status_message(card); /* softsetup */ @@ -1086,9 +1130,6 @@ static int qeth_l2_recover(void *ptr) card->use_hard_stop = 1; __qeth_l2_set_offline(card->gdev, 1); rc = __qeth_l2_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"); @@ -1099,6 +1140,8 @@ static int qeth_l2_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; } |