Skip to content

Commit

Permalink
qeth: NAPI support for l2 and l3 discipline
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Frank Blaschka authored and David S. Miller committed Sep 8, 2010
1 parent 81d5374 commit a1c3ed4
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 138 deletions.
17 changes: 17 additions & 0 deletions drivers/s390/net/qeth_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ enum qeth_discipline_id {
};

struct qeth_discipline {
void (*start_poll)(struct ccw_device *, int, unsigned long);
qdio_handler_t *input_handler;
qdio_handler_t *output_handler;
int (*recover)(void *ptr);
Expand All @@ -702,6 +703,16 @@ struct qeth_skb_data {
#define QETH_SKB_MAGIC 0x71657468
#define QETH_SIGA_CC2_RETRIES 3

struct qeth_rx {
int b_count;
int b_index;
struct qdio_buffer_element *b_element;
int e_offset;
int qdio_err;
};

#define QETH_NAPI_WEIGHT 128

struct qeth_card {
struct list_head list;
enum qeth_card_states state;
Expand Down Expand Up @@ -749,6 +760,8 @@ struct qeth_card {
debug_info_t *debug;
struct mutex conf_mutex;
struct mutex discipline_mutex;
struct napi_struct napi;
struct qeth_rx rx;
};

struct qeth_card_list_struct {
Expand Down Expand Up @@ -831,6 +844,10 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
struct qdio_buffer *, struct qdio_buffer_element **, int *,
struct qeth_hdr **);
void qeth_schedule_recovery(struct qeth_card *);
void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long);
void qeth_qdio_input_handler(struct ccw_device *,
unsigned int, unsigned int, int,
int, unsigned long);
void qeth_qdio_output_handler(struct ccw_device *, unsigned int,
int, int, int, unsigned long);
void qeth_clear_ipacmd_list(struct qeth_card *);
Expand Down
26 changes: 24 additions & 2 deletions drivers/s390/net/qeth_core_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2911,6 +2911,27 @@ static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
}
}

void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue,
unsigned long card_ptr)
{
struct qeth_card *card = (struct qeth_card *)card_ptr;

if (card->dev)
napi_schedule(&card->napi);
}
EXPORT_SYMBOL_GPL(qeth_qdio_start_poll);

void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err,
unsigned int queue, int first_element, int count,
unsigned long card_ptr)
{
struct qeth_card *card = (struct qeth_card *)card_ptr;

if (qdio_err)
qeth_schedule_recovery(card);
}
EXPORT_SYMBOL_GPL(qeth_qdio_input_handler);

void qeth_qdio_output_handler(struct ccw_device *ccwdev,
unsigned int qdio_error, int __queue, int first_element,
int count, unsigned long card_ptr)
Expand Down Expand Up @@ -3843,6 +3864,7 @@ static int qeth_qdio_establish(struct qeth_card *card)
init_data.no_output_qs = card->qdio.no_out_queues;
init_data.input_handler = card->discipline.input_handler;
init_data.output_handler = card->discipline.output_handler;
init_data.queue_start_poll = card->discipline.start_poll;
init_data.int_parm = (unsigned long) card;
init_data.input_sbal_addr_array = (void **) in_sbal_ptrs;
init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
Expand Down Expand Up @@ -4513,8 +4535,8 @@ static struct {
/* 20 */{"queue 1 buffer usage"},
{"queue 2 buffer usage"},
{"queue 3 buffer usage"},
{"rx handler time"},
{"rx handler count"},
{"rx poll time"},
{"rx poll count"},
{"rx do_QDIO time"},
{"rx do_QDIO count"},
{"tx handler time"},
Expand Down
173 changes: 108 additions & 65 deletions drivers/s390/net/qeth_l2_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -755,49 +829,10 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
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)
Expand All @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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");
Expand All @@ -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;
}

Expand Down
Loading

0 comments on commit a1c3ed4

Please sign in to comment.