|
@@ -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;
|
|
|
}
|
|
|
|