diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 20:48:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 20:48:54 -0400 |
commit | 1f1c2881f673671539b25686df463518d69c4649 (patch) | |
tree | 45f4a79f2371ae4525fd621d4b5820732efa161e /drivers/s390 | |
parent | 7608a864e5211df1e3c1948e2719aec7c27b9333 (diff) | |
parent | c5e3ae8823693b260ce1f217adca8add1bc0b3de (diff) |
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
* 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6: (37 commits)
forcedeth bug fix: realtek phy
forcedeth bug fix: vitesse phy
forcedeth bug fix: cicada phy
atl1: reorder atl1_main functions
atl1: fix excessively indented code
atl1: cleanup atl1_main
atl1: header file cleanup
atl1: remove irq_sem
cdc-subset to support new vendor/product ID
8139cp: implement the missing dev->tx_timeout
myri10ge: Remove nonsensical limit in the tx done routine
gianfar: kill unused header
EP93XX_ETH must select MII
macb: Add multicast capability
macb: Use generic PHY layer
s390: add barriers to qeth driver
s390: scatter-gather for inbound traffic in qeth driver
eHEA: Introducing support vor DLPAR memory add
Fix a potential NULL pointer dereference in free_shared_mem() in drivers/net/s2io.c
[PATCH] softmac: Fix ESSID problem
...
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth.h | 9 | ||||
-rw-r--r-- | drivers/s390/net/qeth_main.c | 183 | ||||
-rw-r--r-- | drivers/s390/net/qeth_proc.c | 6 |
3 files changed, 166 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..57f69434fbf9 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 | /** |
@@ -1934,6 +1935,7 @@ qeth_send_control_data(struct qeth_card *card, int len, | |||
1934 | atomic_inc(&reply->received); | 1935 | atomic_inc(&reply->received); |
1935 | wake_up(&reply->wait_q); | 1936 | wake_up(&reply->wait_q); |
1936 | } | 1937 | } |
1938 | cpu_relax(); | ||
1937 | }; | 1939 | }; |
1938 | rc = reply->rc; | 1940 | rc = reply->rc; |
1939 | qeth_put_reply(reply); | 1941 | qeth_put_reply(reply); |
@@ -2258,6 +2260,89 @@ qeth_get_skb(unsigned int length, struct qeth_hdr *hdr) | |||
2258 | return skb; | 2260 | return skb; |
2259 | } | 2261 | } |
2260 | 2262 | ||
2263 | static inline int | ||
2264 | qeth_create_skb_frag(struct qdio_buffer_element *element, | ||
2265 | struct sk_buff **pskb, | ||
2266 | int offset, int *pfrag, int data_len) | ||
2267 | { | ||
2268 | struct page *page = virt_to_page(element->addr); | ||
2269 | if (*pfrag == 0) { | ||
2270 | /* the upper protocol layers assume that there is data in the | ||
2271 | * skb itself. Copy a small amount (64 bytes) to make them | ||
2272 | * happy. */ | ||
2273 | *pskb = dev_alloc_skb(64 + QETH_FAKE_LL_LEN_ETH); | ||
2274 | if (!(*pskb)) | ||
2275 | return -ENOMEM; | ||
2276 | skb_reserve(*pskb, QETH_FAKE_LL_LEN_ETH); | ||
2277 | if (data_len <= 64) { | ||
2278 | memcpy(skb_put(*pskb, data_len), element->addr + offset, | ||
2279 | data_len); | ||
2280 | } else { | ||
2281 | get_page(page); | ||
2282 | memcpy(skb_put(*pskb, 64), element->addr + offset, 64); | ||
2283 | skb_fill_page_desc(*pskb, *pfrag, page, offset + 64, | ||
2284 | data_len - 64); | ||
2285 | (*pskb)->data_len += data_len - 64; | ||
2286 | (*pskb)->len += data_len - 64; | ||
2287 | (*pskb)->truesize += data_len - 64; | ||
2288 | } | ||
2289 | } else { | ||
2290 | get_page(page); | ||
2291 | skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len); | ||
2292 | (*pskb)->data_len += data_len; | ||
2293 | (*pskb)->len += data_len; | ||
2294 | (*pskb)->truesize += data_len; | ||
2295 | } | ||
2296 | (*pfrag)++; | ||
2297 | return 0; | ||
2298 | } | ||
2299 | |||
2300 | static inline struct qeth_buffer_pool_entry * | ||
2301 | qeth_find_free_buffer_pool_entry(struct qeth_card *card) | ||
2302 | { | ||
2303 | struct list_head *plh; | ||
2304 | struct qeth_buffer_pool_entry *entry; | ||
2305 | int i, free; | ||
2306 | struct page *page; | ||
2307 | |||
2308 | if (list_empty(&card->qdio.in_buf_pool.entry_list)) | ||
2309 | return NULL; | ||
2310 | |||
2311 | list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { | ||
2312 | entry = list_entry(plh, struct qeth_buffer_pool_entry, list); | ||
2313 | free = 1; | ||
2314 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { | ||
2315 | if (page_count(virt_to_page(entry->elements[i])) > 1) { | ||
2316 | free = 0; | ||
2317 | break; | ||
2318 | } | ||
2319 | } | ||
2320 | if (free) { | ||
2321 | list_del_init(&entry->list); | ||
2322 | return entry; | ||
2323 | } | ||
2324 | } | ||
2325 | |||
2326 | /* no free buffer in pool so take first one and swap pages */ | ||
2327 | entry = list_entry(card->qdio.in_buf_pool.entry_list.next, | ||
2328 | struct qeth_buffer_pool_entry, list); | ||
2329 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { | ||
2330 | if (page_count(virt_to_page(entry->elements[i])) > 1) { | ||
2331 | page = alloc_page(GFP_ATOMIC|GFP_DMA); | ||
2332 | if (!page) { | ||
2333 | return NULL; | ||
2334 | } else { | ||
2335 | free_page((unsigned long)entry->elements[i]); | ||
2336 | entry->elements[i] = page_address(page); | ||
2337 | if (card->options.performance_stats) | ||
2338 | card->perf_stats.sg_alloc_page_rx++; | ||
2339 | } | ||
2340 | } | ||
2341 | } | ||
2342 | list_del_init(&entry->list); | ||
2343 | return entry; | ||
2344 | } | ||
2345 | |||
2261 | static struct sk_buff * | 2346 | static struct sk_buff * |
2262 | qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | 2347 | qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, |
2263 | struct qdio_buffer_element **__element, int *__offset, | 2348 | struct qdio_buffer_element **__element, int *__offset, |
@@ -2269,6 +2354,8 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2269 | int skb_len; | 2354 | int skb_len; |
2270 | void *data_ptr; | 2355 | void *data_ptr; |
2271 | int data_len; | 2356 | int data_len; |
2357 | int use_rx_sg = 0; | ||
2358 | int frag = 0; | ||
2272 | 2359 | ||
2273 | QETH_DBF_TEXT(trace,6,"nextskb"); | 2360 | QETH_DBF_TEXT(trace,6,"nextskb"); |
2274 | /* qeth_hdr must not cross element boundaries */ | 2361 | /* qeth_hdr must not cross element boundaries */ |
@@ -2293,23 +2380,43 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2293 | 2380 | ||
2294 | if (!skb_len) | 2381 | if (!skb_len) |
2295 | return NULL; | 2382 | return NULL; |
2296 | if (card->options.fake_ll){ | 2383 | if ((skb_len >= card->options.rx_sg_cb) && |
2297 | if(card->dev->type == ARPHRD_IEEE802_TR){ | 2384 | (!(card->info.type == QETH_CARD_TYPE_OSN)) && |
2298 | if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR, *hdr))) | 2385 | (!atomic_read(&card->force_alloc_skb))) { |
2299 | goto no_mem; | 2386 | use_rx_sg = 1; |
2300 | skb_reserve(skb,QETH_FAKE_LL_LEN_TR); | 2387 | } else { |
2388 | if (card->options.fake_ll) { | ||
2389 | if (card->dev->type == ARPHRD_IEEE802_TR) { | ||
2390 | if (!(skb = qeth_get_skb(skb_len + | ||
2391 | QETH_FAKE_LL_LEN_TR, *hdr))) | ||
2392 | goto no_mem; | ||
2393 | skb_reserve(skb, QETH_FAKE_LL_LEN_TR); | ||
2394 | } else { | ||
2395 | if (!(skb = qeth_get_skb(skb_len + | ||
2396 | QETH_FAKE_LL_LEN_ETH, *hdr))) | ||
2397 | goto no_mem; | ||
2398 | skb_reserve(skb, QETH_FAKE_LL_LEN_ETH); | ||
2399 | } | ||
2301 | } else { | 2400 | } else { |
2302 | if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH, *hdr))) | 2401 | skb = qeth_get_skb(skb_len, *hdr); |
2402 | if (!skb) | ||
2303 | goto no_mem; | 2403 | goto no_mem; |
2304 | skb_reserve(skb,QETH_FAKE_LL_LEN_ETH); | ||
2305 | } | 2404 | } |
2306 | } else if (!(skb = qeth_get_skb(skb_len, *hdr))) | 2405 | } |
2307 | goto no_mem; | 2406 | |
2308 | data_ptr = element->addr + offset; | 2407 | data_ptr = element->addr + offset; |
2309 | while (skb_len) { | 2408 | while (skb_len) { |
2310 | data_len = min(skb_len, (int)(element->length - offset)); | 2409 | data_len = min(skb_len, (int)(element->length - offset)); |
2311 | if (data_len) | 2410 | if (data_len) { |
2312 | memcpy(skb_put(skb, data_len), data_ptr, data_len); | 2411 | if (use_rx_sg) { |
2412 | if (qeth_create_skb_frag(element, &skb, offset, | ||
2413 | &frag, data_len)) | ||
2414 | goto no_mem; | ||
2415 | } else { | ||
2416 | memcpy(skb_put(skb, data_len), data_ptr, | ||
2417 | data_len); | ||
2418 | } | ||
2419 | } | ||
2313 | skb_len -= data_len; | 2420 | skb_len -= data_len; |
2314 | if (skb_len){ | 2421 | if (skb_len){ |
2315 | if (qeth_is_last_sbale(element)){ | 2422 | if (qeth_is_last_sbale(element)){ |
@@ -2331,6 +2438,10 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, | |||
2331 | } | 2438 | } |
2332 | *__element = element; | 2439 | *__element = element; |
2333 | *__offset = offset; | 2440 | *__offset = offset; |
2441 | if (use_rx_sg && card->options.performance_stats) { | ||
2442 | card->perf_stats.sg_skbs_rx++; | ||
2443 | card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; | ||
2444 | } | ||
2334 | return skb; | 2445 | return skb; |
2335 | no_mem: | 2446 | no_mem: |
2336 | if (net_ratelimit()){ | 2447 | if (net_ratelimit()){ |
@@ -2608,28 +2719,15 @@ qeth_process_inbound_buffer(struct qeth_card *card, | |||
2608 | } | 2719 | } |
2609 | } | 2720 | } |
2610 | 2721 | ||
2611 | static struct qeth_buffer_pool_entry * | 2722 | 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) | 2723 | qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) |
2628 | { | 2724 | { |
2629 | struct qeth_buffer_pool_entry *pool_entry; | 2725 | struct qeth_buffer_pool_entry *pool_entry; |
2630 | int i; | 2726 | int i; |
2631 | 2727 | ||
2632 | pool_entry = qeth_get_buffer_pool_entry(card); | 2728 | pool_entry = qeth_find_free_buffer_pool_entry(card); |
2729 | if (!pool_entry) | ||
2730 | return 1; | ||
2633 | /* | 2731 | /* |
2634 | * since the buffer is accessed only from the input_tasklet | 2732 | * since the buffer is accessed only from the input_tasklet |
2635 | * there shouldn't be a need to synchronize; also, since we use | 2733 | * there shouldn't be a need to synchronize; also, since we use |
@@ -2648,6 +2746,7 @@ qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) | |||
2648 | buf->buffer->element[i].flags = 0; | 2746 | buf->buffer->element[i].flags = 0; |
2649 | } | 2747 | } |
2650 | buf->state = QETH_QDIO_BUF_EMPTY; | 2748 | buf->state = QETH_QDIO_BUF_EMPTY; |
2749 | return 0; | ||
2651 | } | 2750 | } |
2652 | 2751 | ||
2653 | static void | 2752 | static void |
@@ -2682,6 +2781,7 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) | |||
2682 | int count; | 2781 | int count; |
2683 | int i; | 2782 | int i; |
2684 | int rc; | 2783 | int rc; |
2784 | int newcount = 0; | ||
2685 | 2785 | ||
2686 | QETH_DBF_TEXT(trace,6,"queinbuf"); | 2786 | QETH_DBF_TEXT(trace,6,"queinbuf"); |
2687 | count = (index < queue->next_buf_to_init)? | 2787 | count = (index < queue->next_buf_to_init)? |
@@ -2692,9 +2792,27 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) | |||
2692 | /* only requeue at a certain threshold to avoid SIGAs */ | 2792 | /* only requeue at a certain threshold to avoid SIGAs */ |
2693 | if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ | 2793 | if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ |
2694 | for (i = queue->next_buf_to_init; | 2794 | for (i = queue->next_buf_to_init; |
2695 | i < queue->next_buf_to_init + count; ++i) | 2795 | i < queue->next_buf_to_init + count; ++i) { |
2696 | qeth_init_input_buffer(card, | 2796 | if (qeth_init_input_buffer(card, |
2697 | &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]); | 2797 | &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { |
2798 | break; | ||
2799 | } else { | ||
2800 | newcount++; | ||
2801 | } | ||
2802 | } | ||
2803 | |||
2804 | if (newcount < count) { | ||
2805 | /* we are in memory shortage so we switch back to | ||
2806 | traditional skb allocation and drop packages */ | ||
2807 | if (atomic_cmpxchg(&card->force_alloc_skb, 0, 1)) | ||
2808 | printk(KERN_WARNING | ||
2809 | "qeth: switch to alloc skb\n"); | ||
2810 | count = newcount; | ||
2811 | } else { | ||
2812 | if (atomic_cmpxchg(&card->force_alloc_skb, 1, 0)) | ||
2813 | printk(KERN_WARNING "qeth: switch to sg\n"); | ||
2814 | } | ||
2815 | |||
2698 | /* | 2816 | /* |
2699 | * according to old code it should be avoided to requeue all | 2817 | * according to old code it should be avoided to requeue all |
2700 | * 128 buffers in order to benefit from PCI avoidance. | 2818 | * 128 buffers in order to benefit from PCI avoidance. |
@@ -6494,6 +6612,7 @@ qeth_hardsetup_card(struct qeth_card *card) | |||
6494 | 6612 | ||
6495 | QETH_DBF_TEXT(setup, 2, "hrdsetup"); | 6613 | QETH_DBF_TEXT(setup, 2, "hrdsetup"); |
6496 | 6614 | ||
6615 | atomic_set(&card->force_alloc_skb, 0); | ||
6497 | retry: | 6616 | retry: |
6498 | if (retries < 3){ | 6617 | if (retries < 3){ |
6499 | PRINT_WARN("Retrying to do IDX activates.\n"); | 6618 | 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, |