summaryrefslogtreecommitdiff
path: root/drivers/s390/net/qeth_core_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net/qeth_core_main.c')
-rw-r--r--drivers/s390/net/qeth_core_main.c398
1 files changed, 241 insertions, 157 deletions
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 6d3f2f14b414..24fd17b347fe 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -244,7 +244,7 @@ static struct qeth_buffer_pool_entry *qeth_alloc_pool_entry(unsigned int pages)
return NULL;
for (i = 0; i < pages; i++) {
- entry->elements[i] = alloc_page(GFP_KERNEL);
+ entry->elements[i] = __dev_alloc_page(GFP_KERNEL);
if (!entry->elements[i]) {
qeth_free_pool_entry(entry);
@@ -538,23 +538,16 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
for (i = 0;
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
i++) {
- if (aob->sba[i] && buffer->is_header[i])
- kmem_cache_free(qeth_core_header_cache,
- (void *) aob->sba[i]);
+ void *data = phys_to_virt(aob->sba[i]);
+
+ if (data && buffer->is_header[i])
+ kmem_cache_free(qeth_core_header_cache, data);
}
atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
qdio_release_aob(aob);
}
-static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue)
-{
- return card->options.cq == QETH_CQ_ENABLED &&
- card->qdio.c_q != NULL &&
- queue != 0 &&
- queue == card->qdio.no_in_queues - 1;
-}
-
static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u8 flags, u32 len,
void *data)
{
@@ -813,7 +806,7 @@ static void qeth_issue_next_read_cb(struct qeth_card *card,
/* fall through */
default:
qeth_clear_ipacmd_list(card);
- goto out;
+ goto err_idx;
}
cmd = __ipa_reply(iob);
@@ -866,8 +859,9 @@ out:
memcpy(&card->seqno.pdu_hdr_ack,
QETH_PDU_HEADER_SEQ_NO(iob->data),
QETH_SEQ_NO_LENGTH);
- qeth_put_cmd(iob);
__qeth_issue_next_read(card);
+err_idx:
+ qeth_put_cmd(iob);
}
static int qeth_set_thread_start_bit(struct qeth_card *card,
@@ -1161,17 +1155,20 @@ static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error,
QETH_TXQ_STAT_INC(queue, bufs);
QETH_TXQ_STAT_ADD(queue, buf_elements, buf->next_element_to_fill);
+ if (error) {
+ QETH_TXQ_STAT_ADD(queue, tx_errors, buf->frames);
+ } else {
+ QETH_TXQ_STAT_ADD(queue, tx_packets, buf->frames);
+ QETH_TXQ_STAT_ADD(queue, tx_bytes, buf->bytes);
+ }
+
while ((skb = __skb_dequeue(&buf->skb_list)) != NULL) {
unsigned int bytes = qdisc_pkt_len(skb);
bool is_tso = skb_is_gso(skb);
unsigned int packets;
packets = is_tso ? skb_shinfo(skb)->gso_segs : 1;
- if (error) {
- QETH_TXQ_STAT_ADD(queue, tx_errors, packets);
- } else {
- QETH_TXQ_STAT_ADD(queue, tx_packets, packets);
- QETH_TXQ_STAT_ADD(queue, tx_bytes, bytes);
+ if (!error) {
if (skb->ip_summed == CHECKSUM_PARTIAL)
QETH_TXQ_STAT_ADD(queue, skbs_csum, packets);
if (skb_is_nonlinear(skb))
@@ -1208,6 +1205,7 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
qeth_scrub_qdio_buffer(buf->buffer, queue->max_elements);
buf->next_element_to_fill = 0;
+ buf->frames = 0;
buf->bytes = 0;
atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
}
@@ -1243,9 +1241,12 @@ EXPORT_SYMBOL_GPL(qeth_drain_output_queues);
static int qeth_osa_set_output_queues(struct qeth_card *card, bool single)
{
- unsigned int count = single ? 1 : card->dev->num_tx_queues;
+ unsigned int max = single ? 1 : card->dev->num_tx_queues;
+ unsigned int count;
int rc;
+ count = IS_VM_NIC(card) ? min(max, card->dev->real_num_tx_queues) : max;
+
rtnl_lock();
rc = netif_set_real_num_tx_queues(card->dev, count);
rtnl_unlock();
@@ -1253,16 +1254,16 @@ static int qeth_osa_set_output_queues(struct qeth_card *card, bool single)
if (rc)
return rc;
- if (card->qdio.no_out_queues == count)
+ if (card->qdio.no_out_queues == max)
return 0;
if (atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED)
qeth_free_qdio_queues(card);
- if (count == 1)
+ if (max == 1 && card->qdio.do_prio_queueing != QETH_PRIOQ_DEFAULT)
dev_info(&card->gdev->dev, "Priority Queueing not supported\n");
- card->qdio.no_out_queues = count;
+ card->qdio.no_out_queues = max;
return 0;
}
@@ -1314,7 +1315,6 @@ static void qeth_set_initial_options(struct qeth_card *card)
{
card->options.route4.type = NO_ROUTER;
card->options.route6.type = NO_ROUTER;
- card->options.rx_sg_cb = QETH_RX_SG_CB;
card->options.isolation = ISOLATION_MODE_NONE;
card->options.cq = QETH_CQ_DISABLED;
card->options.layer = QETH_DISCIPLINE_UNDETERMINED;
@@ -1682,17 +1682,16 @@ static void qeth_set_blkt_defaults(struct qeth_card *card)
}
}
-static void qeth_init_tokens(struct qeth_card *card)
+static void qeth_idx_init(struct qeth_card *card)
{
+ memset(&card->seqno, 0, sizeof(card->seqno));
+
card->token.issuer_rm_w = 0x00010103UL;
card->token.cm_filter_w = 0x00010108UL;
card->token.cm_connection_w = 0x0001010aUL;
card->token.ulp_filter_w = 0x0001010bUL;
card->token.ulp_connection_w = 0x0001010dUL;
-}
-static void qeth_init_func_level(struct qeth_card *card)
-{
switch (card->info.type) {
case QETH_CARD_TYPE_IQD:
card->info.func_level = QETH_IDX_FUNC_LEVEL_IQD;
@@ -2405,6 +2404,8 @@ static int qeth_alloc_qdio_queues(struct qeth_card *card)
queue->card = card;
queue->queue_no = i;
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;
/* give outbound qeth_qdio_buffers their qdio_buffers */
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
@@ -2628,15 +2629,13 @@ static void qeth_initialize_working_pool_list(struct qeth_card *card)
static struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry(
struct qeth_card *card)
{
- struct list_head *plh;
struct qeth_buffer_pool_entry *entry;
int i, free;
if (list_empty(&card->qdio.in_buf_pool.entry_list))
return NULL;
- list_for_each(plh, &card->qdio.in_buf_pool.entry_list) {
- entry = list_entry(plh, struct qeth_buffer_pool_entry, list);
+ list_for_each_entry(entry, &card->qdio.in_buf_pool.entry_list, list) {
free = 1;
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
if (page_count(entry->elements[i]) > 1) {
@@ -2651,11 +2650,11 @@ static struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry(
}
/* no free buffer in pool so take first one and swap pages */
- entry = list_entry(card->qdio.in_buf_pool.entry_list.next,
- struct qeth_buffer_pool_entry, list);
+ entry = list_first_entry(&card->qdio.in_buf_pool.entry_list,
+ struct qeth_buffer_pool_entry, list);
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
if (page_count(entry->elements[i]) > 1) {
- struct page *page = alloc_page(GFP_ATOMIC);
+ struct page *page = dev_alloc_page();
if (!page)
return NULL;
@@ -2763,6 +2762,7 @@ static int qeth_init_qdio_queues(struct qeth_card *card)
queue->next_buf_to_fill = 0;
queue->do_pack = 0;
queue->prev_hdr = NULL;
+ queue->coalesced_frames = 0;
queue->bulk_start = 0;
queue->bulk_count = 0;
queue->bulk_max = qeth_tx_select_bulk_max(card, queue);
@@ -3353,16 +3353,21 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
for (i = index; i < index + count; ++i) {
unsigned int bidx = QDIO_BUFNR(i);
+ struct sk_buff *skb;
buf = queue->bufs[bidx];
buf->buffer->element[buf->next_element_to_fill - 1].eflags |=
SBAL_EFLAGS_LAST_ENTRY;
+ queue->coalesced_frames += buf->frames;
if (queue->bufstates)
queue->bufstates[bidx].user = buf;
- if (IS_IQD(queue->card))
+ if (IS_IQD(card)) {
+ skb_queue_walk(&buf->skb_list, skb)
+ skb_tx_timestamp(skb);
continue;
+ }
if (!queue->do_pack) {
if ((atomic_read(&queue->used_buffers) >=
@@ -3390,6 +3395,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
}
}
+ QETH_TXQ_STAT_INC(queue, doorbell);
qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
if (atomic_read(&queue->set_pci_flags_count))
qdio_flags |= QDIO_FLAG_PCI_OUT;
@@ -3397,8 +3403,18 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
queue->queue_no, index, count);
/* Fake the TX completion interrupt: */
- if (IS_IQD(card))
- napi_schedule(&queue->napi);
+ if (IS_IQD(card)) {
+ unsigned int frames = READ_ONCE(queue->max_coalesced_frames);
+ unsigned int 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 (rc) {
/* ignore temporary SIGA errors without busy condition */
@@ -3462,13 +3478,11 @@ static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
}
}
-static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue,
- unsigned long card_ptr)
+static void qeth_qdio_poll(struct ccw_device *cdev, unsigned long card_ptr)
{
struct qeth_card *card = (struct qeth_card *)card_ptr;
- if (card->dev->flags & IFF_UP)
- napi_schedule_irqoff(&card->napi);
+ napi_schedule_irqoff(&card->napi);
}
int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq)
@@ -3502,9 +3516,6 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err,
int i;
int rc;
- if (!qeth_is_cq(card, queue))
- return;
-
QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element);
QETH_CARD_TEXT_(card, 5, "qcqhc%d", count);
QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err);
@@ -3550,9 +3561,7 @@ static void qeth_qdio_input_handler(struct ccw_device *ccwdev,
QETH_CARD_TEXT_(card, 2, "qihq%d", queue);
QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err);
- if (qeth_is_cq(card, queue))
- qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count);
- else if (qdio_err)
+ if (qdio_err)
qeth_schedule_recovery(card);
}
@@ -3635,6 +3644,8 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb)
return ~ntohs(veth->h_vlan_TCI) >>
(VLAN_PRIO_SHIFT + 1) & 3;
break;
+ case QETH_PRIO_Q_ING_FIXED:
+ return card->qdio.default_out_queue;
default:
break;
}
@@ -3707,6 +3718,7 @@ static int qeth_add_hw_header(struct qeth_qdio_out_q *queue,
unsigned int hdr_len, unsigned int proto_len,
unsigned int *elements)
{
+ gfp_t gfp = GFP_ATOMIC | (skb_pfmemalloc(skb) ? __GFP_MEMALLOC : 0);
const unsigned int contiguous = proto_len ? proto_len : 1;
const unsigned int max_elements = queue->max_elements;
unsigned int __elements;
@@ -3762,10 +3774,11 @@ check_layout:
*hdr = skb_push(skb, hdr_len);
return hdr_len;
}
- /* fall back */
+
+ /* Fall back to cache element with known-good alignment: */
if (hdr_len + proto_len > QETH_HDR_CACHE_OBJ_SIZE)
return -E2BIG;
- *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
+ *hdr = kmem_cache_alloc(qeth_core_header_cache, gfp);
if (!*hdr)
return -ENOMEM;
/* Copy protocol headers behind HW header: */
@@ -3948,6 +3961,7 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue,
next_element = qeth_fill_buffer(buffer, skb, hdr, offset, hd_len);
buffer->bytes += bytes;
+ buffer->frames += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
queue->prev_hdr = hdr;
flush = __netdev_tx_sent_queue(txq, bytes,
@@ -4038,6 +4052,8 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
}
next_element = qeth_fill_buffer(buffer, skb, hdr, offset, hd_len);
+ buffer->bytes += qdisc_pkt_len(skb);
+ buffer->frames += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
if (queue->do_pack)
QETH_TXQ_STAT_INC(queue, skbs_pack);
@@ -4797,10 +4813,7 @@ out:
}
static void qeth_qdio_establish_cq(struct qeth_card *card,
- struct qdio_buffer **in_sbal_ptrs,
- void (**queue_start_poll)
- (struct ccw_device *, int,
- unsigned long))
+ struct qdio_buffer **in_sbal_ptrs)
{
int i;
@@ -4811,8 +4824,6 @@ static void qeth_qdio_establish_cq(struct qeth_card *card,
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++)
in_sbal_ptrs[offset + i] =
card->qdio.c_q->bufs[i].buffer;
-
- queue_start_poll[card->qdio.no_in_queues - 1] = NULL;
}
}
@@ -4821,7 +4832,6 @@ static int qeth_qdio_establish(struct qeth_card *card)
struct qdio_initialize init_data;
char *qib_param_field;
struct qdio_buffer **in_sbal_ptrs;
- void (**queue_start_poll) (struct ccw_device *, int, unsigned long);
struct qdio_buffer **out_sbal_ptrs;
int i, j, k;
int rc = 0;
@@ -4848,16 +4858,7 @@ static int qeth_qdio_establish(struct qeth_card *card)
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++)
in_sbal_ptrs[i] = card->qdio.in_q->bufs[i].buffer;
- queue_start_poll = kcalloc(card->qdio.no_in_queues, sizeof(void *),
- GFP_KERNEL);
- if (!queue_start_poll) {
- rc = -ENOMEM;
- goto out_free_in_sbals;
- }
- for (i = 0; i < card->qdio.no_in_queues; ++i)
- queue_start_poll[i] = qeth_qdio_start_poll;
-
- qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll);
+ qeth_qdio_establish_cq(card, in_sbal_ptrs);
out_sbal_ptrs =
kcalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q,
@@ -4865,7 +4866,7 @@ static int qeth_qdio_establish(struct qeth_card *card)
GFP_KERNEL);
if (!out_sbal_ptrs) {
rc = -ENOMEM;
- goto out_free_queue_start_poll;
+ goto out_free_in_sbals;
}
for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i)
@@ -4883,7 +4884,7 @@ static int qeth_qdio_establish(struct qeth_card *card)
init_data.no_output_qs = card->qdio.no_out_queues;
init_data.input_handler = qeth_qdio_input_handler;
init_data.output_handler = qeth_qdio_output_handler;
- init_data.queue_start_poll_array = queue_start_poll;
+ init_data.irq_poll = qeth_qdio_poll;
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;
@@ -4916,8 +4917,6 @@ static int qeth_qdio_establish(struct qeth_card *card)
}
out:
kfree(out_sbal_ptrs);
-out_free_queue_start_poll:
- kfree(queue_start_poll);
out_free_in_sbals:
kfree(in_sbal_ptrs);
out_free_qib_param:
@@ -4952,12 +4951,16 @@ static struct ccw_device_id qeth_ids[] = {
.driver_info = QETH_CARD_TYPE_OSD},
{CCW_DEVICE_DEVTYPE(0x1731, 0x05, 0x1732, 0x05),
.driver_info = QETH_CARD_TYPE_IQD},
+#ifdef CONFIG_QETH_OSN
{CCW_DEVICE_DEVTYPE(0x1731, 0x06, 0x1732, 0x06),
.driver_info = QETH_CARD_TYPE_OSN},
+#endif
{CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x03),
.driver_info = QETH_CARD_TYPE_OSM},
+#ifdef CONFIG_QETH_OSX
{CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x02),
.driver_info = QETH_CARD_TYPE_OSX},
+#endif
{},
};
MODULE_DEVICE_TABLE(ccw, qeth_ids);
@@ -5012,9 +5015,9 @@ retriable:
else
goto retry;
}
+
qeth_determine_capabilities(card);
- qeth_init_tokens(card);
- qeth_init_func_level(card);
+ qeth_idx_init(card);
rc = qeth_idx_activate_read_channel(card);
if (rc == -EINTR) {
@@ -5324,13 +5327,13 @@ static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale)
}
static int qeth_extract_skb(struct qeth_card *card,
- struct qeth_qdio_buffer *qethbuffer,
- struct qdio_buffer_element **__element,
+ struct qeth_qdio_buffer *qethbuffer, u8 *element_no,
int *__offset)
{
- struct qdio_buffer_element *element = *__element;
+ struct qeth_priv *priv = netdev_priv(card->dev);
struct qdio_buffer *buffer = qethbuffer->buffer;
struct napi_struct *napi = &card->napi;
+ struct qdio_buffer_element *element;
unsigned int linear_len = 0;
bool uses_frags = false;
int offset = *__offset;
@@ -5340,6 +5343,8 @@ static int qeth_extract_skb(struct qeth_card *card,
struct sk_buff *skb;
int skb_len = 0;
+ element = &buffer->element[*element_no];
+
next_packet:
/* qeth_hdr must not cross element boundaries */
while (element->length < offset + sizeof(struct qeth_hdr)) {
@@ -5404,7 +5409,7 @@ next_packet:
}
use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) ||
- (skb_len > card->options.rx_sg_cb &&
+ (skb_len > READ_ONCE(priv->rx_copybreak) &&
!atomic_read(&card->force_alloc_skb) &&
!IS_OSN(card));
@@ -5495,22 +5500,20 @@ walk_packet:
if (!skb)
goto next_packet;
- *__element = element;
+ *element_no = element - &buffer->element[0];
*__offset = offset;
qeth_receive_skb(card, skb, hdr, uses_frags);
return 0;
}
-static int qeth_extract_skbs(struct qeth_card *card, int budget,
- struct qeth_qdio_buffer *buf, bool *done)
+static unsigned int qeth_extract_skbs(struct qeth_card *card, int budget,
+ struct qeth_qdio_buffer *buf, bool *done)
{
- int work_done = 0;
-
- *done = false;
+ unsigned int work_done = 0;
while (budget) {
- if (qeth_extract_skb(card, buf, &card->rx.b_element,
+ if (qeth_extract_skb(card, buf, &card->rx.buf_element,
&card->rx.e_offset)) {
*done = true;
break;
@@ -5523,15 +5526,16 @@ static int qeth_extract_skbs(struct qeth_card *card, int budget,
return work_done;
}
-int qeth_poll(struct napi_struct *napi, int budget)
+static unsigned int qeth_rx_poll(struct qeth_card *card, int budget)
{
- struct qeth_card *card = container_of(napi, struct qeth_card, napi);
- int work_done = 0;
- struct qeth_qdio_buffer *buffer;
- int new_budget = budget;
- bool done;
+ unsigned int work_done = 0;
- while (1) {
+ while (budget > 0) {
+ struct qeth_qdio_buffer *buffer;
+ unsigned int skbs_done = 0;
+ bool done = false;
+
+ /* Fetch completed RX buffers: */
if (!card->rx.b_count) {
card->rx.qdio_err = 0;
card->rx.b_count = qdio_get_next_buffers(
@@ -5541,50 +5545,73 @@ int qeth_poll(struct napi_struct *napi, int budget)
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_extract_skbs(card, new_budget,
- buffer, &done);
- else
- done = true;
-
- if (done) {
- QETH_CARD_STAT_INC(card, rx_bufs);
- 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 =
- QDIO_BUFNR(card->rx.b_index + 1);
- card->rx.b_element =
- &card->qdio.in_q
- ->bufs[card->rx.b_index]
- .buffer->element[0];
- card->rx.e_offset = 0;
- }
- }
+ /* Process one completed RX buffer: */
+ 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")))
+ skbs_done = qeth_extract_skbs(card, budget, buffer,
+ &done);
+ else
+ done = true;
- if (work_done >= budget)
- goto out;
- else
- new_budget = budget - work_done;
+ work_done += skbs_done;
+ budget -= skbs_done;
+
+ if (done) {
+ QETH_CARD_STAT_INC(card, rx_bufs);
+ qeth_put_buffer_pool_entry(card, buffer->pool_entry);
+ qeth_queue_input_buffer(card, card->rx.b_index);
+ card->rx.b_count--;
+
+ /* Step forward to next buffer: */
+ card->rx.b_index = QDIO_BUFNR(card->rx.b_index + 1);
+ card->rx.buf_element = 0;
+ card->rx.e_offset = 0;
}
}
+ return work_done;
+}
+
+static void qeth_cq_poll(struct qeth_card *card)
+{
+ unsigned int work_done = 0;
+
+ while (work_done < QDIO_MAX_BUFFERS_PER_Q) {
+ unsigned int start, error;
+ int completed;
+
+ completed = qdio_inspect_queue(CARD_DDEV(card), 1, true, &start,
+ &error);
+ if (completed <= 0)
+ return;
+
+ qeth_qdio_cq_handler(card, error, 1, start, completed);
+ work_done += completed;
+ }
+}
+
+int qeth_poll(struct napi_struct *napi, int budget)
+{
+ struct qeth_card *card = container_of(napi, struct qeth_card, napi);
+ unsigned int work_done;
+
+ work_done = qeth_rx_poll(card, budget);
+
+ if (card->options.cq == QETH_CQ_ENABLED)
+ qeth_cq_poll(card);
+
+ /* Exhausted the RX budget. Keep IRQ disabled, we get called again. */
+ if (budget && work_done >= budget)
+ return work_done;
+
if (napi_complete_done(napi, work_done) &&
- qdio_start_irq(CARD_DDEV(card), 0))
+ qdio_start_irq(CARD_DDEV(card)))
napi_schedule(napi);
-out:
+
return work_done;
}
EXPORT_SYMBOL_GPL(qeth_poll);
@@ -5658,7 +5685,7 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget)
if (completed <= 0) {
/* Ensure we see TX completion for pending work: */
if (napi_complete_done(napi, 0))
- qeth_tx_arm_timer(queue);
+ qeth_tx_arm_timer(queue, QETH_TX_TIMER_USECS);
return 0;
}
@@ -5667,7 +5694,7 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget)
unsigned int bidx = QDIO_BUFNR(i);
buffer = queue->bufs[bidx];
- packets += skb_queue_len(&buffer->skb_list);
+ packets += buffer->frames;
bytes += buffer->bytes;
qeth_handle_send_error(card, buffer, error);
@@ -5953,25 +5980,30 @@ static void qeth_clear_dbf_list(void)
static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
{
struct net_device *dev;
+ struct qeth_priv *priv;
switch (card->info.type) {
case QETH_CARD_TYPE_IQD:
- dev = alloc_netdev_mqs(0, "hsi%d", NET_NAME_UNKNOWN,
+ dev = alloc_netdev_mqs(sizeof(*priv), "hsi%d", NET_NAME_UNKNOWN,
ether_setup, QETH_MAX_QUEUES, 1);
break;
case QETH_CARD_TYPE_OSM:
- dev = alloc_etherdev(0);
+ dev = alloc_etherdev(sizeof(*priv));
break;
case QETH_CARD_TYPE_OSN:
- dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup);
+ dev = alloc_netdev(sizeof(*priv), "osn%d", NET_NAME_UNKNOWN,
+ ether_setup);
break;
default:
- dev = alloc_etherdev_mqs(0, QETH_MAX_QUEUES, 1);
+ dev = alloc_etherdev_mqs(sizeof(*priv), QETH_MAX_QUEUES, 1);
}
if (!dev)
return NULL;
+ priv = netdev_priv(dev);
+ priv->rx_copybreak = QETH_RX_COPYBREAK;
+
dev->ml_priv = card;
dev->watchdog_timeo = QETH_TX_TIMEOUT;
dev->min_mtu = IS_OSN(card) ? 64 : 576;
@@ -5981,22 +6013,8 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
SET_NETDEV_DEV(dev, &card->gdev->dev);
netif_carrier_off(dev);
- if (IS_OSN(card)) {
- dev->ethtool_ops = &qeth_osn_ethtool_ops;
- } else {
- dev->ethtool_ops = &qeth_ethtool_ops;
- dev->priv_flags &= ~IFF_TX_SKB_SHARING;
- dev->hw_features |= NETIF_F_SG;
- dev->vlan_features |= NETIF_F_SG;
- if (IS_IQD(card)) {
- dev->features |= NETIF_F_SG;
- if (netif_set_real_num_tx_queues(dev,
- QETH_IQD_MIN_TXQ)) {
- free_netdev(dev);
- return NULL;
- }
- }
- }
+ dev->ethtool_ops = IS_OSN(card) ? &qeth_osn_ethtool_ops :
+ &qeth_ethtool_ops;
return dev;
}
@@ -6012,6 +6030,28 @@ struct net_device *qeth_clone_netdev(struct net_device *orig)
return clone;
}
+int qeth_setup_netdev(struct qeth_card *card)
+{
+ struct net_device *dev = card->dev;
+ unsigned int num_tx_queues;
+
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ dev->hw_features |= NETIF_F_SG;
+ dev->vlan_features |= NETIF_F_SG;
+
+ if (IS_IQD(card)) {
+ dev->features |= NETIF_F_SG;
+ num_tx_queues = QETH_IQD_MIN_TXQ;
+ } else if (IS_VM_NIC(card)) {
+ num_tx_queues = 1;
+ } else {
+ num_tx_queues = dev->real_num_tx_queues;
+ }
+
+ return qeth_set_real_num_tx_queues(card, num_tx_queues);
+}
+EXPORT_SYMBOL_GPL(qeth_setup_netdev);
+
static int qeth_core_probe_device(struct ccwgroup_device *gdev)
{
struct qeth_card *card;
@@ -6051,12 +6091,13 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
goto err_card;
}
+ qeth_determine_capabilities(card);
+ qeth_set_blkt_defaults(card);
+
card->qdio.no_out_queues = card->dev->num_tx_queues;
rc = qeth_update_from_chp_desc(card);
if (rc)
goto err_chp_desc;
- qeth_determine_capabilities(card);
- qeth_set_blkt_defaults(card);
enforced_disc = qeth_enforce_discipline(card);
switch (enforced_disc) {
@@ -6241,9 +6282,6 @@ int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
struct mii_ioctl_data *mii_data;
int rc = 0;
- if (!card)
- return -ENODEV;
-
switch (cmd) {
case SIOC_QETH_ADP_SET_SNMP_CONTROL:
rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
@@ -6623,12 +6661,59 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
}
EXPORT_SYMBOL_GPL(qeth_get_stats64);
+#define TC_IQD_UCAST 0
+static void qeth_iqd_set_prio_tc_map(struct net_device *dev,
+ unsigned int ucast_txqs)
+{
+ unsigned int prio;
+
+ /* IQD requires mcast traffic to be placed on a dedicated queue, and
+ * qeth_iqd_select_queue() deals with this.
+ * For unicast traffic, we defer the queue selection to the stack.
+ * By installing a trivial prio map that spans over only the unicast
+ * queues, we can encourage the stack to spread the ucast traffic evenly
+ * without selecting the mcast queue.
+ */
+
+ /* One traffic class, spanning over all active ucast queues: */
+ netdev_set_num_tc(dev, 1);
+ netdev_set_tc_queue(dev, TC_IQD_UCAST, ucast_txqs,
+ QETH_IQD_MIN_UCAST_TXQ);
+
+ /* Map all priorities to this traffic class: */
+ for (prio = 0; prio <= TC_BITMASK; prio++)
+ netdev_set_prio_tc_map(dev, prio, TC_IQD_UCAST);
+}
+
+int qeth_set_real_num_tx_queues(struct qeth_card *card, unsigned int count)
+{
+ struct net_device *dev = card->dev;
+ int rc;
+
+ /* Per netif_setup_tc(), adjust the mapping first: */
+ if (IS_IQD(card))
+ qeth_iqd_set_prio_tc_map(dev, count - 1);
+
+ rc = netif_set_real_num_tx_queues(dev, count);
+
+ if (rc && IS_IQD(card))
+ qeth_iqd_set_prio_tc_map(dev, dev->real_num_tx_queues - 1);
+
+ return rc;
+}
+
u16 qeth_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
u8 cast_type, struct net_device *sb_dev)
{
+ u16 txq;
+
if (cast_type != RTN_UNICAST)
return QETH_IQD_MCAST_TXQ;
- return QETH_IQD_MIN_UCAST_TXQ;
+ if (dev->real_num_tx_queues == QETH_IQD_MIN_TXQ)
+ return QETH_IQD_MIN_UCAST_TXQ;
+
+ txq = netdev_pick_tx(dev, skb, sb_dev);
+ return (txq == QETH_IQD_MCAST_TXQ) ? QETH_IQD_MIN_UCAST_TXQ : txq;
}
EXPORT_SYMBOL_GPL(qeth_iqd_select_queue);
@@ -6638,9 +6723,6 @@ int qeth_open(struct net_device *dev)
QETH_CARD_TEXT(card, 4, "qethopen");
- if (qdio_stop_irq(CARD_DDEV(card), 0) < 0)
- return -EIO;
-
card->data.state = CH_STATE_UP;
netif_tx_start_all_queues(dev);
@@ -6690,6 +6772,8 @@ int qeth_stop(struct net_device *dev)
}
napi_disable(&card->napi);
+ qdio_stop_irq(CARD_DDEV(card));
+
return 0;
}
EXPORT_SYMBOL_GPL(qeth_stop);