diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth.h | 9 | ||||
-rw-r--r-- | drivers/s390/net/qeth_main.c | 182 | ||||
-rw-r--r-- | drivers/s390/net/qeth_proc.c | 6 |
3 files changed, 165 insertions, 32 deletions
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index b34eb82edd98..ec18bae05df0 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h | |||
@@ -211,6 +211,10 @@ struct qeth_perf_stats { | |||
211 | /* initial values when measuring starts */ | 211 | /* initial values when measuring starts */ |
212 | unsigned long initial_rx_packets; | 212 | unsigned long initial_rx_packets; |
213 | unsigned long initial_tx_packets; | 213 | unsigned long initial_tx_packets; |
214 | /* inbound scatter gather data */ | ||
215 | unsigned int sg_skbs_rx; | ||
216 | unsigned int sg_frags_rx; | ||
217 | unsigned int sg_alloc_page_rx; | ||
214 | }; | 218 | }; |
215 | 219 | ||
216 | /* Routing stuff */ | 220 | /* Routing stuff */ |
@@ -341,6 +345,9 @@ qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) | |||
341 | 345 | ||
342 | #define QETH_IP_HEADER_SIZE 40 | 346 | #define QETH_IP_HEADER_SIZE 40 |
343 | 347 | ||
348 | /* large receive scatter gather copy break */ | ||
349 | #define QETH_RX_SG_CB (PAGE_SIZE >> 1) | ||
350 | |||
344 | struct qeth_hdr_layer3 { | 351 | struct qeth_hdr_layer3 { |
345 | __u8 id; | 352 | __u8 id; |
346 | __u8 flags; | 353 | __u8 flags; |
@@ -771,6 +778,7 @@ struct qeth_card_options { | |||
771 | int layer2; | 778 | int layer2; |
772 | enum qeth_large_send_types large_send; | 779 | enum qeth_large_send_types large_send; |
773 | int performance_stats; | 780 | int performance_stats; |
781 | int rx_sg_cb; | ||
774 | }; | 782 | }; |
775 | 783 | ||
776 | /* | 784 | /* |
@@ -828,6 +836,7 @@ struct qeth_card { | |||
828 | int (*orig_hard_header)(struct sk_buff *,struct net_device *, | 836 | int (*orig_hard_header)(struct sk_buff *,struct net_device *, |
829 | unsigned short,void *,void *,unsigned); | 837 | unsigned short,void *,void *,unsigned); |
830 | struct qeth_osn_info osn_info; | 838 | struct qeth_osn_info osn_info; |
839 | atomic_t force_alloc_skb; | ||
831 | }; | 840 | }; |
832 | 841 | ||
833 | struct qeth_card_list_struct { | 842 | struct qeth_card_list_struct { |
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 86b0c44165c1..8dca93598ea3 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c | |||
@@ -1054,6 +1054,7 @@ qeth_set_intial_options(struct qeth_card *card) | |||
1054 | else | 1054 | else |
1055 | card->options.layer2 = 0; | 1055 | card->options.layer2 = 0; |
1056 | card->options.performance_stats = 0; | 1056 | card->options.performance_stats = 0; |
1057 | card->options.rx_sg_cb = QETH_RX_SG_CB; | ||
1057 | } | 1058 | } |
1058 | 1059 | ||
1059 | /** | 1060 | /** |
@@ -2258,6 +2259,89 @@ qeth_get_skb(unsigned int length, struct qeth_hdr *hdr) | |||
2258 | return skb; | 2259 | return skb; |
2259 | } | 2260 | } |
2260 | 2261 | ||
2262 | static inline int | ||
2263 | qeth_create_skb_frag(struct qdio_buffer_element *element, | ||
2264 | struct sk_buff **pskb, | ||
2265 | int offset, int *pfrag, int data_len) | ||
2266 | { | ||
2267 | struct page *page = virt_to_page(element->addr); | ||
2268 | if (*pfrag == 0) { | ||
2269 | /* the upper protocol layers assume that there is data in the | ||
2270 | * skb itself. Copy a small amount (64 bytes) to make them | ||
2271 | * happy. */ | ||
2272 | *pskb = dev_alloc_skb(64 + QETH_FAKE_LL_LEN_ETH); | ||
2273 | if (!(*pskb)) | ||
2274 | return -ENOMEM; | ||
2275 | skb_reserve(*pskb, QETH_FAKE_LL_LEN_ETH); | ||
2276 | if (data_len <= 64) { | ||
2277 | memcpy(skb_put(*pskb, data_len), element->addr + offset, | ||
2278 | data_len); | ||
2279 | } else { | ||
2280 | get_page(page); | ||
2281 | memcpy(skb_put(*pskb, 64), element->addr + offset, 64); | ||
2282 | skb_fill_page_desc(*pskb, *pfrag, page, offset + 64, | ||
2283 | data_len - 64); | ||
2284 | (*pskb)->data_len += data_len - 64; | ||
2285 | (*pskb)->len += data_len - 64; | ||
2286 | (*pskb)->truesize += data_len - 64; | ||
2287 | } | ||
2288 | } else { | ||
2289 | get_page(page); | ||
2290 | skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len); | ||
2291 | (*pskb)->data_len += data_len; | ||
2292 | (*pskb)->len += data_len; | ||
2293 | (*pskb)->truesize += data_len; | ||
2294 | } | ||
2295 | (*pfrag)++; | ||
2296 | return 0; | ||
2297 | } | ||
2298 | |||
2299 | static inline struct qeth_buffer_pool_entry * | ||
2300 | qeth_find_free_buffer_pool_entry(struct qeth_card *card) | ||
2301 | { | ||
2302 | struct list_head *plh; | ||
2303 | struct qeth_buffer_pool_entry *entry; | ||
2304 | int i, free; | ||
2305 | struct page *page; | ||
2306 | |||
2307 | if (list_empty(&card->qdio.in_buf_pool.entry_list)) | ||
2308 | return NULL; | ||
2309 | |||
2310 | list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { | ||
2311 | entry = list_entry(plh, struct qeth_buffer_pool_entry, list); | ||
2312 | free = 1; | ||
2313 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { | ||
2314 | if (page_count(virt_to_page(entry->elements[i])) > 1) { | ||
2315 | free = 0; | ||
2316 | break; | ||
2317 | } | ||
2318 | } | ||
2319 | if (free) { | ||
2320 | list_del_init(&entry->list); | ||
2321 | return entry; | ||
2322 | } | ||
2323 | } | ||
2324 | |||
2325 | /* no free buffer in pool so take first one and swap pages */ | ||
2326 | entry = list_entry(card->qdio.in_buf_pool.entry_list.next, | ||
2327 | struct qeth_buffer_pool_entry, list); | ||
2328 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { | ||
2329 | if (page_count(virt_to_page(entry->elements[i])) > 1) { | ||
2330 | page = alloc_page(GFP_ATOMIC|GFP_DMA); | ||
2331 | if (!page) { | ||
2332 | return NULL; | ||
2333 | } else { | ||
2334 | free_page((unsigned long)entry->elements[i]); | ||
2335 | entry->elements[i] = page_address(page); | ||
2336 | if (card->options.performance_stats) | ||
2337 | card->perf_stats.sg_alloc_page_rx++; | ||
2338 | } | ||
2339 | } | ||
2340 | } | ||
2341 | list_del_init(&entry->list); | ||
2342 | return entry; | ||
2343 | } | ||
2344 | |||
2261 | static struct sk_buff * | 2345 | static struct sk_buff * |
2262 | qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | 2346 | qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, |
2263 | struct qdio_buffer_element **__element, int *__offset, | 2347 | struct qdio_buffer_element **__element, int *__offset, |
@@ -2269,6 +2353,8 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2269 | int skb_len; | 2353 | int skb_len; |
2270 | void *data_ptr; | 2354 | void *data_ptr; |
2271 | int data_len; | 2355 | int data_len; |
2356 | int use_rx_sg = 0; | ||
2357 | int frag = 0; | ||
2272 | 2358 | ||
2273 | QETH_DBF_TEXT(trace,6,"nextskb"); | 2359 | QETH_DBF_TEXT(trace,6,"nextskb"); |
2274 | /* qeth_hdr must not cross element boundaries */ | 2360 | /* qeth_hdr must not cross element boundaries */ |
@@ -2293,23 +2379,43 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2293 | 2379 | ||
2294 | if (!skb_len) | 2380 | if (!skb_len) |
2295 | return NULL; | 2381 | return NULL; |
2296 | if (card->options.fake_ll){ | 2382 | if ((skb_len >= card->options.rx_sg_cb) && |
2297 | if(card->dev->type == ARPHRD_IEEE802_TR){ | 2383 | (!(card->info.type == QETH_CARD_TYPE_OSN)) && |
2298 | if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR, *hdr))) | 2384 | (!atomic_read(&card->force_alloc_skb))) { |
2299 | goto no_mem; | 2385 | use_rx_sg = 1; |
2300 | skb_reserve(skb,QETH_FAKE_LL_LEN_TR); | 2386 | } else { |
2387 | if (card->options.fake_ll) { | ||
2388 | if (card->dev->type == ARPHRD_IEEE802_TR) { | ||
2389 | if (!(skb = qeth_get_skb(skb_len + | ||
2390 | QETH_FAKE_LL_LEN_TR, *hdr))) | ||
2391 | goto no_mem; | ||
2392 | skb_reserve(skb, QETH_FAKE_LL_LEN_TR); | ||
2393 | } else { | ||
2394 | if (!(skb = qeth_get_skb(skb_len + | ||
2395 | QETH_FAKE_LL_LEN_ETH, *hdr))) | ||
2396 | goto no_mem; | ||
2397 | skb_reserve(skb, QETH_FAKE_LL_LEN_ETH); | ||
2398 | } | ||
2301 | } else { | 2399 | } else { |
2302 | if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH, *hdr))) | 2400 | skb = qeth_get_skb(skb_len, *hdr); |
2401 | if (!skb) | ||
2303 | goto no_mem; | 2402 | goto no_mem; |
2304 | skb_reserve(skb,QETH_FAKE_LL_LEN_ETH); | ||
2305 | } | 2403 | } |
2306 | } else if (!(skb = qeth_get_skb(skb_len, *hdr))) | 2404 | } |
2307 | goto no_mem; | 2405 | |
2308 | data_ptr = element->addr + offset; | 2406 | data_ptr = element->addr + offset; |
2309 | while (skb_len) { | 2407 | while (skb_len) { |
2310 | data_len = min(skb_len, (int)(element->length - offset)); | 2408 | data_len = min(skb_len, (int)(element->length - offset)); |
2311 | if (data_len) | 2409 | if (data_len) { |
2312 | memcpy(skb_put(skb, data_len), data_ptr, data_len); | 2410 | if (use_rx_sg) { |
2411 | if (qeth_create_skb_frag(element, &skb, offset, | ||
2412 | &frag, data_len)) | ||
2413 | goto no_mem; | ||
2414 | } else { | ||
2415 | memcpy(skb_put(skb, data_len), data_ptr, | ||
2416 | data_len); | ||
2417 | } | ||
2418 | } | ||
2313 | skb_len -= data_len; | 2419 | skb_len -= data_len; |
2314 | if (skb_len){ | 2420 | if (skb_len){ |
2315 | if (qeth_is_last_sbale(element)){ | 2421 | if (qeth_is_last_sbale(element)){ |
@@ -2331,6 +2437,10 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2331 | } | 2437 | } |
2332 | *__element = element; | 2438 | *__element = element; |
2333 | *__offset = offset; | 2439 | *__offset = offset; |
2440 | if (use_rx_sg && card->options.performance_stats) { | ||
2441 | card->perf_stats.sg_skbs_rx++; | ||
2442 | card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; | ||
2443 | } | ||
2334 | return skb; | 2444 | return skb; |
2335 | no_mem: | 2445 | no_mem: |
2336 | if (net_ratelimit()){ | 2446 | if (net_ratelimit()){ |
@@ -2608,28 +2718,15 @@ qeth_process_inbound_buffer(struct qeth_card *card, | |||
2608 | } | 2718 | } |
2609 | } | 2719 | } |
2610 | 2720 | ||
2611 | static struct qeth_buffer_pool_entry * | 2721 | static int |
2612 | qeth_get_buffer_pool_entry(struct qeth_card *card) | ||
2613 | { | ||
2614 | struct qeth_buffer_pool_entry *entry; | ||
2615 | |||
2616 | QETH_DBF_TEXT(trace, 6, "gtbfplen"); | ||
2617 | if (!list_empty(&card->qdio.in_buf_pool.entry_list)) { | ||
2618 | entry = list_entry(card->qdio.in_buf_pool.entry_list.next, | ||
2619 | struct qeth_buffer_pool_entry, list); | ||
2620 | list_del_init(&entry->list); | ||
2621 | return entry; | ||
2622 | } | ||
2623 | return NULL; | ||
2624 | } | ||
2625 | |||
2626 | static void | ||
2627 | qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) | 2722 | qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) |
2628 | { | 2723 | { |
2629 | struct qeth_buffer_pool_entry *pool_entry; | 2724 | struct qeth_buffer_pool_entry *pool_entry; |
2630 | int i; | 2725 | int i; |
2631 | 2726 | ||
2632 | pool_entry = qeth_get_buffer_pool_entry(card); | 2727 | pool_entry = qeth_find_free_buffer_pool_entry(card); |
2728 | if (!pool_entry) | ||
2729 | return 1; | ||
2633 | /* | 2730 | /* |
2634 | * since the buffer is accessed only from the input_tasklet | 2731 | * since the buffer is accessed only from the input_tasklet |
2635 | * there shouldn't be a need to synchronize; also, since we use | 2732 | * there shouldn't be a need to synchronize; also, since we use |
@@ -2648,6 +2745,7 @@ qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) | |||
2648 | buf->buffer->element[i].flags = 0; | 2745 | buf->buffer->element[i].flags = 0; |
2649 | } | 2746 | } |
2650 | buf->state = QETH_QDIO_BUF_EMPTY; | 2747 | buf->state = QETH_QDIO_BUF_EMPTY; |
2748 | return 0; | ||
2651 | } | 2749 | } |
2652 | 2750 | ||
2653 | static void | 2751 | static void |
@@ -2682,6 +2780,7 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) | |||
2682 | int count; | 2780 | int count; |
2683 | int i; | 2781 | int i; |
2684 | int rc; | 2782 | int rc; |
2783 | int newcount = 0; | ||
2685 | 2784 | ||
2686 | QETH_DBF_TEXT(trace,6,"queinbuf"); | 2785 | QETH_DBF_TEXT(trace,6,"queinbuf"); |
2687 | count = (index < queue->next_buf_to_init)? | 2786 | count = (index < queue->next_buf_to_init)? |
@@ -2692,9 +2791,27 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) | |||
2692 | /* only requeue at a certain threshold to avoid SIGAs */ | 2791 | /* only requeue at a certain threshold to avoid SIGAs */ |
2693 | if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ | 2792 | if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ |
2694 | for (i = queue->next_buf_to_init; | 2793 | for (i = queue->next_buf_to_init; |
2695 | i < queue->next_buf_to_init + count; ++i) | 2794 | i < queue->next_buf_to_init + count; ++i) { |
2696 | qeth_init_input_buffer(card, | 2795 | if (qeth_init_input_buffer(card, |
2697 | &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]); | 2796 | &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { |
2797 | break; | ||
2798 | } else { | ||
2799 | newcount++; | ||
2800 | } | ||
2801 | } | ||
2802 | |||
2803 | if (newcount < count) { | ||
2804 | /* we are in memory shortage so we switch back to | ||
2805 | traditional skb allocation and drop packages */ | ||
2806 | if (atomic_cmpxchg(&card->force_alloc_skb, 0, 1)) | ||
2807 | printk(KERN_WARNING | ||
2808 | "qeth: switch to alloc skb\n"); | ||
2809 | count = newcount; | ||
2810 | } else { | ||
2811 | if (atomic_cmpxchg(&card->force_alloc_skb, 1, 0)) | ||
2812 | printk(KERN_WARNING "qeth: switch to sg\n"); | ||
2813 | } | ||
2814 | |||
2698 | /* | 2815 | /* |
2699 | * according to old code it should be avoided to requeue all | 2816 | * according to old code it should be avoided to requeue all |
2700 | * 128 buffers in order to benefit from PCI avoidance. | 2817 | * 128 buffers in order to benefit from PCI avoidance. |
@@ -6494,6 +6611,7 @@ qeth_hardsetup_card(struct qeth_card *card) | |||
6494 | 6611 | ||
6495 | QETH_DBF_TEXT(setup, 2, "hrdsetup"); | 6612 | QETH_DBF_TEXT(setup, 2, "hrdsetup"); |
6496 | 6613 | ||
6614 | atomic_set(&card->force_alloc_skb, 0); | ||
6497 | retry: | 6615 | retry: |
6498 | if (retries < 3){ | 6616 | if (retries < 3){ |
6499 | PRINT_WARN("Retrying to do IDX activates.\n"); | 6617 | PRINT_WARN("Retrying to do IDX activates.\n"); |
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index 89d56c8ecdd2..f1ff165a5e05 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c | |||
@@ -212,6 +212,12 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it) | |||
212 | " Skb fragments sent in SG mode : %u\n\n", | 212 | " Skb fragments sent in SG mode : %u\n\n", |
213 | card->perf_stats.sg_skbs_sent, | 213 | card->perf_stats.sg_skbs_sent, |
214 | card->perf_stats.sg_frags_sent); | 214 | card->perf_stats.sg_frags_sent); |
215 | seq_printf(s, " Skbs received in SG mode : %u\n" | ||
216 | " Skb fragments received in SG mode : %u\n" | ||
217 | " Page allocations for rx SG mode : %u\n\n", | ||
218 | card->perf_stats.sg_skbs_rx, | ||
219 | card->perf_stats.sg_frags_rx, | ||
220 | card->perf_stats.sg_alloc_page_rx); | ||
215 | seq_printf(s, " large_send tx (in Kbytes) : %u\n" | 221 | seq_printf(s, " large_send tx (in Kbytes) : %u\n" |
216 | " large_send count : %u\n\n", | 222 | " large_send count : %u\n\n", |
217 | card->perf_stats.large_send_bytes >> 10, | 223 | card->perf_stats.large_send_bytes >> 10, |