diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 18:39:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 18:39:49 -0700 |
commit | 1c8c5a9d38f607c0b6fd12c91cbe1a4418762a21 (patch) | |
tree | dcc97181d4d187252e0cc8fdf29d9b365fa3ffd0 /drivers/s390 | |
parent | 285767604576148fc1be7fcd112e4a90eb0d6ad2 (diff) | |
parent | 7170e6045a6a8b33f4fa5753589dc77b16198e2d (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) Add Maglev hashing scheduler to IPVS, from Inju Song.
2) Lots of new TC subsystem tests from Roman Mashak.
3) Add TCP zero copy receive and fix delayed acks and autotuning with
SO_RCVLOWAT, from Eric Dumazet.
4) Add XDP_REDIRECT support to mlx5 driver, from Jesper Dangaard
Brouer.
5) Add ttl inherit support to vxlan, from Hangbin Liu.
6) Properly separate ipv6 routes into their logically independant
components. fib6_info for the routing table, and fib6_nh for sets of
nexthops, which thus can be shared. From David Ahern.
7) Add bpf_xdp_adjust_tail helper, which can be used to generate ICMP
messages from XDP programs. From Nikita V. Shirokov.
8) Lots of long overdue cleanups to the r8169 driver, from Heiner
Kallweit.
9) Add BTF ("BPF Type Format"), from Martin KaFai Lau.
10) Add traffic condition monitoring to iwlwifi, from Luca Coelho.
11) Plumb extack down into fib_rules, from Roopa Prabhu.
12) Add Flower classifier offload support to igb, from Vinicius Costa
Gomes.
13) Add UDP GSO support, from Willem de Bruijn.
14) Add documentation for eBPF helpers, from Quentin Monnet.
15) Add TLS tx offload to mlx5, from Ilya Lesokhin.
16) Allow applications to be given the number of bytes available to read
on a socket via a control message returned from recvmsg(), from
Soheil Hassas Yeganeh.
17) Add x86_32 eBPF JIT compiler, from Wang YanQing.
18) Add AF_XDP sockets, with zerocopy support infrastructure as well.
From Björn Töpel.
19) Remove indirect load support from all of the BPF JITs and handle
these operations in the verifier by translating them into native BPF
instead. From Daniel Borkmann.
20) Add GRO support to ipv6 gre tunnels, from Eran Ben Elisha.
21) Allow XDP programs to do lookups in the main kernel routing tables
for forwarding. From David Ahern.
22) Allow drivers to store hardware state into an ELF section of kernel
dump vmcore files, and use it in cxgb4. From Rahul Lakkireddy.
23) Various RACK and loss detection improvements in TCP, from Yuchung
Cheng.
24) Add TCP SACK compression, from Eric Dumazet.
25) Add User Mode Helper support and basic bpfilter infrastructure, from
Alexei Starovoitov.
26) Support ports and protocol values in RTM_GETROUTE, from Roopa
Prabhu.
27) Support bulking in ->ndo_xdp_xmit() API, from Jesper Dangaard
Brouer.
28) Add lots of forwarding selftests, from Petr Machata.
29) Add generic network device failover driver, from Sridhar Samudrala.
* ra.kernel.org:/pub/scm/linux/kernel/git/davem/net-next: (1959 commits)
strparser: Add __strp_unpause and use it in ktls.
rxrpc: Fix terminal retransmission connection ID to include the channel
net: hns3: Optimize PF CMDQ interrupt switching process
net: hns3: Fix for VF mailbox receiving unknown message
net: hns3: Fix for VF mailbox cannot receiving PF response
bnx2x: use the right constant
Revert "net: sched: cls: Fix offloading when ingress dev is vxlan"
net: dsa: b53: Fix for brcm tag issue in Cygnus SoC
enic: fix UDP rss bits
netdev-FAQ: clarify DaveM's position for stable backports
rtnetlink: validate attributes in do_setlink()
mlxsw: Add extack messages for port_{un, }split failures
netdevsim: Add extack error message for devlink reload
devlink: Add extack to reload and port_{un, }split operations
net: metrics: add proper netlink validation
ipmr: fix error path when ipmr_new_table fails
ip6mr: only set ip6mr_table from setsockopt when ip6mr_new_table succeeds
net: hns3: remove unused hclgevf_cfg_func_mta_filter
netfilter: provide udp*_lib_lookup for nf_tproxy
qed*: Utilize FW 8.37.2.0
...
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/lcs.c | 3 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 61 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 148 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_sys.c | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 98 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 207 |
7 files changed, 229 insertions, 292 deletions
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0ee8f33efb54..2d9fe7e4ee40 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1928,6 +1928,8 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char return -EINVAL; /* TODO: sanity checks */ card->portno = value; + if (card->dev) + card->dev->dev_port = card->portno; return count; @@ -2158,6 +2160,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) card->dev = dev; card->dev->ml_priv = card; card->dev->netdev_ops = &lcs_netdev_ops; + card->dev->dev_port = card->portno; memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH); #ifdef CONFIG_IP_MULTICAST if (!lcs_check_multicast_support(card)) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 78b98b3e7efa..2a5fec55bf60 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -148,6 +148,7 @@ struct qeth_perf_stats { unsigned int tx_csum; unsigned int tx_lin; unsigned int tx_linfail; + unsigned int rx_csum; }; /* Routing stuff */ @@ -712,9 +713,6 @@ enum qeth_discipline_id { struct qeth_discipline { const struct device_type *devtype; - void (*start_poll)(struct ccw_device *, int, unsigned long); - qdio_handler_t *input_handler; - qdio_handler_t *output_handler; int (*process_rx_buffer)(struct qeth_card *card, int budget, int *done); int (*recover)(void *ptr); int (*setup) (struct ccwgroup_device *); @@ -780,9 +778,9 @@ struct qeth_card { struct qeth_card_options options; wait_queue_head_t wait_q; - spinlock_t vlanlock; spinlock_t mclock; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + struct mutex vid_list_mutex; /* vid_list */ struct list_head vid_list; DECLARE_HASHTABLE(mac_htable, 4); DECLARE_HASHTABLE(ip_htable, 4); @@ -867,6 +865,32 @@ static inline int qeth_get_ip_version(struct sk_buff *skb) } } +static inline void qeth_rx_csum(struct qeth_card *card, struct sk_buff *skb, + u8 flags) +{ + if ((card->dev->features & NETIF_F_RXCSUM) && + (flags & QETH_HDR_EXT_CSUM_TRANSP_REQ)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (card->options.performance_stats) + card->perf_stats.rx_csum++; + } else { + skb->ip_summed = CHECKSUM_NONE; + } +} + +static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv) +{ + *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; + if ((ipv == 4 && ip_hdr(skb)->protocol == IPPROTO_UDP) || + (ipv == 6 && ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)) + *flags |= QETH_HDR_EXT_UDP; + if (ipv == 4) { + /* some HW requires combined L3+L4 csum offload: */ + *flags |= QETH_HDR_EXT_CSUM_HDR_REQ; + ip_hdr(skb)->check = 0; + } +} + static inline void qeth_put_buffer_pool_entry(struct qeth_card *card, struct qeth_buffer_pool_entry *entry) { @@ -879,6 +903,27 @@ static inline int qeth_is_diagass_supported(struct qeth_card *card, return card->info.diagass_support & (__u32)cmd; } +int qeth_send_simple_setassparms_prot(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + u16 cmd_code, long data, + enum qeth_prot_versions prot); +/* IPv4 variant */ +static inline int qeth_send_simple_setassparms(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + u16 cmd_code, long data) +{ + return qeth_send_simple_setassparms_prot(card, ipa_func, cmd_code, + data, QETH_PROT_IPV4); +} + +static inline int qeth_send_simple_setassparms_v6(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + u16 cmd_code, long data) +{ + return qeth_send_simple_setassparms_prot(card, ipa_func, cmd_code, + data, QETH_PROT_IPV6); +} + extern struct qeth_discipline qeth_l2_discipline; extern struct qeth_discipline qeth_l3_discipline; extern const struct attribute_group *qeth_generic_attr_groups[]; @@ -921,13 +966,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, struct qeth_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); int qeth_poll(struct napi_struct *napi, int budget); -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 *); int qeth_qdio_clear_card(struct qeth_card *, int); void qeth_clear_working_pool_list(struct qeth_card *); @@ -979,8 +1018,6 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action); int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot); void qeth_trace_features(struct qeth_card *); void qeth_close_dev(struct qeth_card *); -int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs, - __u16, long); int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16, long, int (*reply_cb)(struct qeth_card *, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f5a0d894d9ad..9f28b6f2efc4 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1467,13 +1467,13 @@ static int qeth_setup_card(struct qeth_card *card) card->lan_online = 0; card->read_or_write_problem = 0; card->dev = NULL; - spin_lock_init(&card->vlanlock); spin_lock_init(&card->mclock); spin_lock_init(&card->lock); spin_lock_init(&card->ip_lock); spin_lock_init(&card->thread_mask_lock); mutex_init(&card->conf_mutex); mutex_init(&card->discipline_mutex); + mutex_init(&card->vid_list_mutex); card->thread_start_mask = 0; card->thread_allowed_mask = 0; card->thread_running_mask = 0; @@ -3588,15 +3588,14 @@ 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) +static 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 && (card->dev->flags & IFF_UP)) napi_schedule(&card->napi); } -EXPORT_SYMBOL_GPL(qeth_qdio_start_poll); int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) { @@ -3698,9 +3697,10 @@ out: return; } -void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err, - unsigned int queue, int first_elem, int count, - unsigned long card_ptr) +static void qeth_qdio_input_handler(struct ccw_device *ccwdev, + unsigned int qdio_err, int queue, + int first_elem, int count, + unsigned long card_ptr) { struct qeth_card *card = (struct qeth_card *)card_ptr; @@ -3711,14 +3711,12 @@ void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err, qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count); else 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) +static void qeth_qdio_output_handler(struct ccw_device *ccwdev, + unsigned int qdio_error, int __queue, + int first_element, int count, + unsigned long card_ptr) { struct qeth_card *card = (struct qeth_card *) card_ptr; struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; @@ -3787,7 +3785,6 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, card->perf_stats.outbound_handler_time += qeth_get_micros() - card->perf_stats.outbound_handler_start_time; } -EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); /* We cannot use outbound queue 3 for unicast packets on HiperSockets */ static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num) @@ -4995,7 +4992,7 @@ static int qeth_qdio_establish(struct qeth_card *card) goto out_free_in_sbals; } for (i = 0; i < card->qdio.no_in_queues; ++i) - queue_start_poll[i] = card->discipline->start_poll; + queue_start_poll[i] = qeth_qdio_start_poll; qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); @@ -5019,8 +5016,8 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.qib_param_field = qib_param_field; init_data.no_input_qs = card->qdio.no_in_queues; 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.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.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; @@ -5204,6 +5201,11 @@ retriable: rc = qeth_query_ipassists(card, QETH_PROT_IPV4); if (rc == -ENOMEM) goto out; + if (qeth_is_supported(card, IPA_IPV6)) { + rc = qeth_query_ipassists(card, QETH_PROT_IPV6); + if (rc == -ENOMEM) + goto out; + } if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { rc = qeth_query_setadapterparms(card); if (rc < 0) { @@ -5511,26 +5513,26 @@ int qeth_send_setassparms(struct qeth_card *card, } EXPORT_SYMBOL_GPL(qeth_send_setassparms); -int qeth_send_simple_setassparms(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, - __u16 cmd_code, long data) +int qeth_send_simple_setassparms_prot(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + u16 cmd_code, long data, + enum qeth_prot_versions prot) { int rc; int length = 0; struct qeth_cmd_buffer *iob; - QETH_CARD_TEXT(card, 4, "simassp4"); + QETH_CARD_TEXT_(card, 4, "simassp%i", prot); if (data) length = sizeof(__u32); - iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, - length, QETH_PROT_IPV4); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, length, prot); if (!iob) return -ENOMEM; rc = qeth_send_setassparms(card, iob, length, data, qeth_setassparms_cb, NULL); return rc; } -EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms); +EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms_prot); static void qeth_unregister_dbf_views(void) { @@ -6008,7 +6010,8 @@ static struct { {"tx lin"}, {"tx linfail"}, {"cq handler count"}, - {"cq handler time"} + {"cq handler time"}, + {"rx csum"} }; int qeth_core_get_sset_count(struct net_device *dev, int stringset) @@ -6070,6 +6073,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[35] = card->perf_stats.tx_linfail; data[36] = card->perf_stats.cq_cnt; data[37] = card->perf_stats.cq_time; + data[38] = card->perf_stats.rx_csum; } EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); @@ -6326,14 +6330,15 @@ static int qeth_ipa_checksum_run_cmd_cb(struct qeth_card *card, static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data, - struct qeth_checksum_cmd *chksum_cb) + struct qeth_checksum_cmd *chksum_cb, + enum qeth_prot_versions prot) { struct qeth_cmd_buffer *iob; int rc = -ENOMEM; QETH_CARD_TEXT(card, 4, "chkdocmd"); iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, - sizeof(__u32), QETH_PROT_IPV4); + sizeof(__u32), prot); if (iob) rc = qeth_send_setassparms(card, iob, sizeof(__u32), data, qeth_ipa_checksum_run_cmd_cb, @@ -6341,16 +6346,17 @@ static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, return rc; } -static int qeth_send_checksum_on(struct qeth_card *card, int cstype) +static int qeth_send_checksum_on(struct qeth_card *card, int cstype, + enum qeth_prot_versions prot) { - const __u32 required_features = QETH_IPA_CHECKSUM_IP_HDR | - QETH_IPA_CHECKSUM_UDP | - QETH_IPA_CHECKSUM_TCP; + u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP; struct qeth_checksum_cmd chksum_cb; int rc; + if (prot == QETH_PROT_IPV4) + required_features |= QETH_IPA_CHECKSUM_IP_HDR; rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, - &chksum_cb); + &chksum_cb, prot); if (!rc) { if ((required_features & chksum_cb.supported) != required_features) @@ -6362,37 +6368,42 @@ static int qeth_send_checksum_on(struct qeth_card *card, int cstype) QETH_CARD_IFNAME(card)); } if (rc) { - qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); + qeth_send_simple_setassparms_prot(card, cstype, + IPA_CMD_ASS_STOP, 0, prot); dev_warn(&card->gdev->dev, - "Starting HW checksumming for %s failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); + "Starting HW IPv%d checksumming for %s failed, using SW checksumming\n", + prot, QETH_CARD_IFNAME(card)); return rc; } rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE, - chksum_cb.supported, &chksum_cb); + chksum_cb.supported, &chksum_cb, + prot); if (!rc) { if ((required_features & chksum_cb.enabled) != required_features) rc = -EIO; } if (rc) { - qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); + qeth_send_simple_setassparms_prot(card, cstype, + IPA_CMD_ASS_STOP, 0, prot); dev_warn(&card->gdev->dev, - "Enabling HW checksumming for %s failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); + "Enabling HW IPv%d checksumming for %s failed, using SW checksumming\n", + prot, QETH_CARD_IFNAME(card)); return rc; } - dev_info(&card->gdev->dev, "HW Checksumming (%sbound) enabled\n", - cstype == IPA_INBOUND_CHECKSUM ? "in" : "out"); + dev_info(&card->gdev->dev, "HW Checksumming (%sbound IPv%d) enabled\n", + cstype == IPA_INBOUND_CHECKSUM ? "in" : "out", prot); return 0; } -static int qeth_set_ipa_csum(struct qeth_card *card, int on, int cstype) +static int qeth_set_ipa_csum(struct qeth_card *card, bool on, int cstype, + enum qeth_prot_versions prot) { - int rc = (on) ? qeth_send_checksum_on(card, cstype) - : qeth_send_simple_setassparms(card, cstype, - IPA_CMD_ASS_STOP, 0); + int rc = (on) ? qeth_send_checksum_on(card, cstype, prot) + : qeth_send_simple_setassparms_prot(card, cstype, + IPA_CMD_ASS_STOP, 0, + prot); return rc ? -EIO : 0; } @@ -6419,8 +6430,31 @@ static int qeth_set_ipa_tso(struct qeth_card *card, int on) return rc; } -#define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO) +static int qeth_set_ipa_rx_csum(struct qeth_card *card, bool on) +{ + int rc_ipv4 = (on) ? -EOPNOTSUPP : 0; + int rc_ipv6; + + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + rc_ipv4 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, + QETH_PROT_IPV4); + if (!qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) + /* no/one Offload Assist available, so the rc is trivial */ + return rc_ipv4; + rc_ipv6 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, + QETH_PROT_IPV6); + + if (on) + /* enable: success if any Assist is active */ + return (rc_ipv6) ? rc_ipv4 : 0; + + /* disable: failure if any Assist is still active */ + return (rc_ipv6) ? rc_ipv6 : rc_ipv4; +} + +#define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO | \ + NETIF_F_IPV6_CSUM) /** * qeth_recover_features() - Restore device features after recovery * @dev: the recovering net_device @@ -6455,16 +6489,19 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features) QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); if ((changed & NETIF_F_IP_CSUM)) { - rc = qeth_set_ipa_csum(card, - features & NETIF_F_IP_CSUM ? 1 : 0, - IPA_OUTBOUND_CHECKSUM); + rc = qeth_set_ipa_csum(card, features & NETIF_F_IP_CSUM, + IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4); if (rc) changed ^= NETIF_F_IP_CSUM; } - if ((changed & NETIF_F_RXCSUM)) { - rc = qeth_set_ipa_csum(card, - features & NETIF_F_RXCSUM ? 1 : 0, - IPA_INBOUND_CHECKSUM); + if (changed & NETIF_F_IPV6_CSUM) { + rc = qeth_set_ipa_csum(card, features & NETIF_F_IPV6_CSUM, + IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6); + if (rc) + changed ^= NETIF_F_IPV6_CSUM; + } + if (changed & NETIF_F_RXCSUM) { + rc = qeth_set_ipa_rx_csum(card, features & NETIF_F_RXCSUM); if (rc) changed ^= NETIF_F_RXCSUM; } @@ -6491,7 +6528,10 @@ netdev_features_t qeth_fix_features(struct net_device *dev, QETH_DBF_TEXT(SETUP, 2, "fixfeat"); if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) features &= ~NETIF_F_IP_CSUM; - if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + if (!qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) + features &= ~NETIF_F_IPV6_CSUM; + if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM) && + !qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) features &= ~NETIF_F_RXCSUM; if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) features &= ~NETIF_F_TSO; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index f4d1ec0b8f5a..878e62f35169 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -246,6 +246,8 @@ enum qeth_ipa_funcs { IPA_QUERY_ARP_ASSIST = 0x00040000L, IPA_INBOUND_TSO = 0x00080000L, IPA_OUTBOUND_TSO = 0x00100000L, + IPA_INBOUND_CHECKSUM_V6 = 0x00400000L, + IPA_OUTBOUND_CHECKSUM_V6 = 0x00800000L, }; /* SETIP/DELIP IPA Command: ***************************************************/ diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index ae81534de912..c3f18afb368b 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -144,6 +144,8 @@ static ssize_t qeth_dev_portno_store(struct device *dev, goto out; } card->info.portno = portno; + if (card->dev) + card->dev->dev_port = portno; out: mutex_unlock(&card->conf_mutex); return rc ? rc : count; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index b8079f2a65b3..a7cb37da6a21 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/etherdevice.h> -#include <linux/ip.h> #include <linux/list.h> #include <linux/hash.h> #include <linux/hashtable.h> @@ -195,23 +194,6 @@ static int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb) return RTN_UNSPEC; } -static void qeth_l2_hdr_csum(struct qeth_card *card, struct qeth_hdr *hdr, - struct sk_buff *skb) -{ - struct iphdr *iph = ip_hdr(skb); - - /* tcph->check contains already the pseudo hdr checksum - * so just set the header flags - */ - if (iph->protocol == IPPROTO_UDP) - hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP; - hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ | - QETH_HDR_EXT_CSUM_HDR_REQ; - iph->check = 0; - if (card->options.performance_stats) - card->perf_stats.tx_csum++; -} - static void qeth_l2_fill_header(struct qeth_hdr *hdr, struct sk_buff *skb, int cast_type, unsigned int data_len) { @@ -297,12 +279,13 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i, static void qeth_l2_process_vlans(struct qeth_card *card) { struct qeth_vlan_vid *id; + QETH_CARD_TEXT(card, 3, "L2prcvln"); - spin_lock_bh(&card->vlanlock); + mutex_lock(&card->vid_list_mutex); list_for_each_entry(id, &card->vid_list, list) { qeth_l2_send_setdelvlan(card, id->vid, IPA_CMD_SETVLAN); } - spin_unlock_bh(&card->vlanlock); + mutex_unlock(&card->vid_list_mutex); } static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, @@ -319,7 +302,7 @@ static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, QETH_CARD_TEXT(card, 3, "aidREC"); return 0; } - id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC); + id = kmalloc(sizeof(*id), GFP_KERNEL); if (id) { id->vid = vid; rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN); @@ -327,9 +310,9 @@ static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, kfree(id); return rc; } - spin_lock_bh(&card->vlanlock); + mutex_lock(&card->vid_list_mutex); list_add_tail(&id->list, &card->vid_list); - spin_unlock_bh(&card->vlanlock); + mutex_unlock(&card->vid_list_mutex); } else { return -ENOMEM; } @@ -348,7 +331,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, QETH_CARD_TEXT(card, 3, "kidREC"); return 0; } - spin_lock_bh(&card->vlanlock); + mutex_lock(&card->vid_list_mutex); list_for_each_entry(id, &card->vid_list, list) { if (id->vid == vid) { list_del(&id->list); @@ -356,7 +339,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, break; } } - spin_unlock_bh(&card->vlanlock); + mutex_unlock(&card->vid_list_mutex); if (tmpid) { rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); kfree(tmpid); @@ -423,15 +406,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, switch (hdr->hdr.l2.id) { case QETH_HEADER_TYPE_LAYER2: skb->protocol = eth_type_trans(skb, skb->dev); - if ((card->dev->features & NETIF_F_RXCSUM) - && ((hdr->hdr.l2.flags[1] & - (QETH_HDR_EXT_CSUM_HDR_REQ | - QETH_HDR_EXT_CSUM_TRANSP_REQ)) == - (QETH_HDR_EXT_CSUM_HDR_REQ | - QETH_HDR_EXT_CSUM_TRANSP_REQ))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; + qeth_rx_csum(card, skb, hdr->hdr.l2.flags[1]); if (skb->protocol == htons(ETH_P_802_2)) *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; len = skb->len; @@ -464,7 +439,6 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, static int qeth_l2_request_initial_mac(struct qeth_card *card) { int rc = 0; - char vendor_pre[] = {0x02, 0x00, 0x00}; QETH_DBF_TEXT(SETUP, 2, "l2reqmac"); QETH_DBF_TEXT_(SETUP, 2, "doL2%s", CARD_BUS_ID(card)); @@ -484,16 +458,20 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card) card->info.type == QETH_CARD_TYPE_OSX || card->info.guestlan) { rc = qeth_setadpparms_change_macaddr(card); - if (rc) { - QETH_DBF_MESSAGE(2, "couldn't get MAC address on " - "device %s: x%x\n", CARD_BUS_ID(card), rc); - QETH_DBF_TEXT_(SETUP, 2, "1err%04x", rc); - return rc; - } - } else { - eth_random_addr(card->dev->dev_addr); - memcpy(card->dev->dev_addr, vendor_pre, 3); + if (!rc) + goto out; + QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %s: x%x\n", + CARD_BUS_ID(card), rc); + QETH_DBF_TEXT_(SETUP, 2, "1err%04x", rc); + /* fall back once more: */ } + + /* some devices don't support a custom MAC address: */ + if (card->info.type == QETH_CARD_TYPE_OSM || + card->info.type == QETH_CARD_TYPE_OSX) + return (rc) ? rc : -EADDRNOTAVAIL; + eth_hw_addr_random(card->dev); + out: QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, card->dev->addr_len); return 0; @@ -685,7 +663,8 @@ out: } static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, - struct qeth_qdio_out_q *queue, int cast_type) + struct qeth_qdio_out_q *queue, int cast_type, + int ipv) { int push_len = sizeof(struct qeth_hdr); unsigned int elements, nr_frags; @@ -723,8 +702,11 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, hdr_elements = 1; } qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len); - if (skb->ip_summed == CHECKSUM_PARTIAL) - qeth_l2_hdr_csum(card, hdr, skb); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv); + if (card->options.performance_stats) + card->perf_stats.tx_csum++; + } elements = qeth_get_elements_no(card, skb, hdr_elements, 0); if (!elements) { @@ -776,6 +758,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, { struct qeth_card *card = dev->ml_priv; int cast_type = qeth_l2_get_cast_type(card, skb); + int ipv = qeth_get_ip_version(skb); struct qeth_qdio_out_q *queue; int tx_bytes = skb->len; int rc; @@ -783,7 +766,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, if (card->qdio.do_prio_queueing || (cast_type && card->info.is_multicast_different)) queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb, - qeth_get_ip_version(skb), cast_type)]; + ipv, cast_type)]; else queue = card->qdio.out_qs[card->qdio.default_out_queue]; @@ -806,7 +789,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type); break; default: - rc = qeth_l2_xmit_osa(card, skb, queue, cast_type); + rc = qeth_l2_xmit_osa(card, skb, queue, cast_type, ipv); } if (!rc) { @@ -983,6 +966,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->mtu = card->info.initial_mtu; card->dev->min_mtu = 64; card->dev->max_mtu = ETH_MAX_MTU; + card->dev->dev_port = card->info.portno; card->dev->netdev_ops = &qeth_l2_netdev_ops; if (card->info.type == QETH_CARD_TYPE_OSN) { card->dev->ethtool_ops = &qeth_l2_osn_ops; @@ -1011,10 +995,15 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->hw_features |= NETIF_F_IP_CSUM; card->dev->vlan_features |= NETIF_F_IP_CSUM; } - if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) { - card->dev->hw_features |= NETIF_F_RXCSUM; - card->dev->vlan_features |= NETIF_F_RXCSUM; - } + } + if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) { + card->dev->hw_features |= NETIF_F_IPV6_CSUM; + card->dev->vlan_features |= NETIF_F_IPV6_CSUM; + } + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) || + qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) { + card->dev->hw_features |= NETIF_F_RXCSUM; + card->dev->vlan_features |= NETIF_F_RXCSUM; } card->info.broadcast_capable = 1; @@ -1315,9 +1304,6 @@ static int qeth_l2_control_event(struct qeth_card *card, struct qeth_discipline qeth_l2_discipline = { .devtype = &qeth_l2_devtype, - .start_poll = qeth_qdio_start_poll, - .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, - .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, .process_rx_buffer = qeth_l2_process_inbound_buffer, .recover = qeth_l2_recover, .setup = qeth_l2_probe_device, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c1a16a74aa83..e7fa479adf47 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -735,22 +735,6 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card) return rc; } -static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, __u16 cmd_code) -{ - int rc; - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 4, "simassp6"); - iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, - 0, QETH_PROT_IPV6); - if (!iob) - return -ENOMEM; - rc = qeth_send_setassparms(card, iob, 0, 0, - qeth_setassparms_cb, NULL); - return rc; -} - static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) { int rc; @@ -851,14 +835,6 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card) QETH_CARD_TEXT(card, 3, "softipv6"); - rc = qeth_query_ipassists(card, QETH_PROT_IPV6); - if (rc) { - dev_err(&card->gdev->dev, - "Activating IPv6 support for %s failed\n", - QETH_CARD_IFNAME(card)); - return rc; - } - if (card->info.type == QETH_CARD_TYPE_IQD) goto out; @@ -870,16 +846,16 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card) QETH_CARD_IFNAME(card)); return rc; } - rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6, - IPA_CMD_ASS_START); + rc = qeth_send_simple_setassparms_v6(card, IPA_IPV6, + IPA_CMD_ASS_START, 0); if (rc) { dev_err(&card->gdev->dev, "Activating IPv6 support for %s failed\n", QETH_CARD_IFNAME(card)); return rc; } - rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU, - IPA_CMD_ASS_START); + rc = qeth_send_simple_setassparms_v6(card, IPA_PASSTHRU, + IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, "Enabling the passthrough mode for %s failed\n", @@ -1293,91 +1269,6 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) in6_dev_put(in6_dev); } -static void qeth_l3_free_vlan_addresses4(struct qeth_card *card, - unsigned short vid) -{ - struct in_device *in_dev; - struct in_ifaddr *ifa; - struct qeth_ipaddr *addr; - struct net_device *netdev; - - QETH_CARD_TEXT(card, 4, "frvaddr4"); - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); - if (!netdev) - return; - in_dev = in_dev_get(netdev); - if (!in_dev) - return; - - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!addr) - goto out; - - spin_lock_bh(&card->ip_lock); - - for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - addr->u.a4.addr = be32_to_cpu(ifa->ifa_address); - addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask); - addr->type = QETH_IP_TYPE_NORMAL; - qeth_l3_delete_ip(card, addr); - } - - spin_unlock_bh(&card->ip_lock); - - kfree(addr); -out: - in_dev_put(in_dev); -} - -static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, - unsigned short vid) -{ - struct inet6_dev *in6_dev; - struct inet6_ifaddr *ifa; - struct qeth_ipaddr *addr; - struct net_device *netdev; - - QETH_CARD_TEXT(card, 4, "frvaddr6"); - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); - if (!netdev) - return; - - in6_dev = in6_dev_get(netdev); - if (!in6_dev) - return; - - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); - if (!addr) - goto out; - - spin_lock_bh(&card->ip_lock); - - list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { - memcpy(&addr->u.a6.addr, &ifa->addr, - sizeof(struct in6_addr)); - addr->u.a6.pfxlen = ifa->prefix_len; - addr->type = QETH_IP_TYPE_NORMAL; - qeth_l3_delete_ip(card, addr); - } - - spin_unlock_bh(&card->ip_lock); - - kfree(addr); -out: - in6_dev_put(in6_dev); -} - -static void qeth_l3_free_vlan_addresses(struct qeth_card *card, - unsigned short vid) -{ - rcu_read_lock(); - qeth_l3_free_vlan_addresses4(card, vid); - qeth_l3_free_vlan_addresses6(card, vid); - rcu_read_unlock(); -} - static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { @@ -1398,8 +1289,6 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, QETH_CARD_TEXT(card, 3, "kidREC"); return 0; } - /* unregister IP addresses of vlan device */ - qeth_l3_free_vlan_addresses(card, vid); clear_bit(vid, card->active_vlans); qeth_l3_set_rx_mode(dev); return 0; @@ -1454,17 +1343,7 @@ static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag); } - if (card->dev->features & NETIF_F_RXCSUM) { - if ((hdr->hdr.l3.ext_flags & - (QETH_HDR_EXT_CSUM_HDR_REQ | - QETH_HDR_EXT_CSUM_TRANSP_REQ)) == - (QETH_HDR_EXT_CSUM_HDR_REQ | - QETH_HDR_EXT_CSUM_TRANSP_REQ)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - } else - skb->ip_summed = CHECKSUM_NONE; + qeth_rx_csum(card, skb, hdr->hdr.l3.ext_flags); } static int qeth_l3_process_inbound_buffer(struct qeth_card *card, @@ -2210,23 +2089,6 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, rcu_read_unlock(); } -static void qeth_l3_hdr_csum(struct qeth_card *card, struct qeth_hdr *hdr, - struct sk_buff *skb) -{ - struct iphdr *iph = ip_hdr(skb); - - /* tcph->check contains already the pseudo hdr checksum - * so just set the header flags - */ - if (iph->protocol == IPPROTO_UDP) - hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP; - hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ | - QETH_HDR_EXT_CSUM_HDR_REQ; - iph->check = 0; - if (card->options.performance_stats) - card->perf_stats.tx_csum++; -} - static void qeth_tso_fill_header(struct qeth_card *card, struct qeth_hdr *qhdr, struct sk_buff *skb) { @@ -2418,8 +2280,11 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, } } - if (skb->ip_summed == CHECKSUM_PARTIAL) - qeth_l3_hdr_csum(card, hdr, new_skb); + if (new_skb->ip_summed == CHECKSUM_PARTIAL) { + qeth_tx_csum(new_skb, &hdr->hdr.l3.ext_flags, ipv); + if (card->options.performance_stats) + card->perf_stats.tx_csum++; + } } elements = use_tso ? @@ -2620,28 +2485,32 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) (card->info.link_type == QETH_LINK_TYPE_HSTR)) { pr_info("qeth_l3: ignoring TR device\n"); return -ENODEV; - } else { - card->dev = alloc_etherdev(0); - if (!card->dev) - return -ENODEV; - card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; - - /*IPv6 address autoconfiguration stuff*/ - qeth_l3_get_unique_id(card); - if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD)) - card->dev->dev_id = card->info.unique_id & - 0xffff; - - card->dev->hw_features |= NETIF_F_SG; - card->dev->vlan_features |= NETIF_F_SG; - - if (!card->info.guestlan) { - card->dev->features |= NETIF_F_SG; - card->dev->hw_features |= NETIF_F_TSO | - NETIF_F_RXCSUM | NETIF_F_IP_CSUM; - card->dev->vlan_features |= NETIF_F_TSO | - NETIF_F_RXCSUM | NETIF_F_IP_CSUM; - } + } + + card->dev = alloc_etherdev(0); + if (!card->dev) + return -ENODEV; + card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; + + /*IPv6 address autoconfiguration stuff*/ + qeth_l3_get_unique_id(card); + if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD)) + card->dev->dev_id = card->info.unique_id & 0xffff; + + card->dev->hw_features |= NETIF_F_SG; + card->dev->vlan_features |= NETIF_F_SG; + + if (!card->info.guestlan) { + card->dev->features |= NETIF_F_SG; + card->dev->hw_features |= NETIF_F_TSO | + NETIF_F_RXCSUM | NETIF_F_IP_CSUM; + card->dev->vlan_features |= NETIF_F_TSO | + NETIF_F_RXCSUM | NETIF_F_IP_CSUM; + } + + if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) { + card->dev->hw_features |= NETIF_F_IPV6_CSUM; + card->dev->vlan_features |= NETIF_F_IPV6_CSUM; } } else if (card->info.type == QETH_CARD_TYPE_IQD) { card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, @@ -2663,6 +2532,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev->mtu = card->info.initial_mtu; card->dev->min_mtu = 64; card->dev->max_mtu = ETH_MAX_MTU; + card->dev->dev_port = card->info.portno; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | @@ -2960,9 +2830,6 @@ static int qeth_l3_control_event(struct qeth_card *card, struct qeth_discipline qeth_l3_discipline = { .devtype = &qeth_l3_devtype, - .start_poll = qeth_qdio_start_poll, - .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, - .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, .process_rx_buffer = qeth_l3_process_inbound_buffer, .recover = qeth_l3_recover, .setup = qeth_l3_probe_device, |