diff options
Diffstat (limited to 'drivers/s390/net/qeth_l3_main.c')
| -rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 3546 |
1 files changed, 1009 insertions, 2537 deletions
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c1b0b2761f8d..027bc346232f 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright IBM Corp. 2007, 2009 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, @@ -6,9 +7,9 @@ * Frank Blaschka <frank.blaschka@de.ibm.com> */ -#define KMSG_COMPONENT "qeth" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "qeth: " fmt +#include <linux/export.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/bitops.h> @@ -16,141 +17,57 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/etherdevice.h> -#include <linux/mii.h> #include <linux/ip.h> +#include <linux/in.h> +#include <linux/inet.h> #include <linux/ipv6.h> #include <linux/inetdevice.h> #include <linux/igmp.h> #include <linux/slab.h> +#include <linux/if_ether.h> #include <linux/if_vlan.h> +#include <linux/skbuff.h> #include <net/ip.h> #include <net/arp.h> #include <net/route.h> -#include <net/ip6_fib.h> -#include <net/ip6_checksum.h> +#include <net/ipv6.h> +#include <net/ip6_route.h> #include <net/iucv/af_iucv.h> +#include <linux/hashtable.h> #include "qeth_l3.h" - -static int qeth_l3_set_offline(struct ccwgroup_device *); -static int qeth_l3_recover(void *); -static int qeth_l3_stop(struct net_device *); -static void qeth_l3_set_multicast_list(struct net_device *); -static int qeth_l3_neigh_setup(struct net_device *, struct neigh_parms *); static int qeth_l3_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *); static int qeth_l3_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *); -static int __qeth_l3_set_online(struct ccwgroup_device *, int); -static int __qeth_l3_set_offline(struct ccwgroup_device *, int); - -static int qeth_l3_isxdigit(char *buf) -{ - while (*buf) { - if (!isxdigit(*buf++)) - return 0; - } - return 1; -} - -void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf) -{ - sprintf(buf, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]); -} - -int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr) -{ - int count = 0, rc = 0; - int in[4]; - char c; - - rc = sscanf(buf, "%u.%u.%u.%u%c", - &in[0], &in[1], &in[2], &in[3], &c); - if (rc != 4 && (rc != 5 || c != '\n')) - return -EINVAL; - for (count = 0; count < 4; count++) { - if (in[count] > 255) - return -EINVAL; - addr[count] = in[count]; - } - return 0; -} -void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf) +int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, + char *buf) { - sprintf(buf, "%pI6", addr); + if (proto == QETH_PROT_IPV4) + return scnprintf(buf, INET_ADDRSTRLEN, "%pI4", addr); + else + return scnprintf(buf, INET6_ADDRSTRLEN, "%pI6", addr); } -int qeth_l3_string_to_ipaddr6(const char *buf, __u8 *addr) +static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, + struct qeth_ipaddr *query) { - const char *end, *end_tmp, *start; - __u16 *in; - char num[5]; - int num2, cnt, out, found, save_cnt; - unsigned short in_tmp[8] = {0, }; + u32 key = qeth_l3_ipaddr_hash(query); + struct qeth_ipaddr *addr; - cnt = out = found = save_cnt = num2 = 0; - end = start = buf; - in = (__u16 *) addr; - memset(in, 0, 16); - while (*end) { - end = strchr(start, ':'); - if (end == NULL) { - end = buf + strlen(buf); - end_tmp = strchr(start, '\n'); - if (end_tmp != NULL) - end = end_tmp; - out = 1; - } - if ((end - start)) { - memset(num, 0, 5); - if ((end - start) > 4) - return -EINVAL; - memcpy(num, start, end - start); - if (!qeth_l3_isxdigit(num)) - return -EINVAL; - sscanf(start, "%x", &num2); - if (found) - in_tmp[save_cnt++] = num2; - else - in[cnt++] = num2; - if (out) - break; - } else { - if (found) - return -EINVAL; - found = 1; - } - start = ++end; + if (query->is_multicast) { + hash_for_each_possible(card->rx_mode_addrs, addr, hnode, key) + if (qeth_l3_addr_match_ip(addr, query)) + return addr; + } else { + hash_for_each_possible(card->ip_htable, addr, hnode, key) + if (qeth_l3_addr_match_ip(addr, query)) + return addr; } - if (cnt + save_cnt > 8) - return -EINVAL; - cnt = 7; - while (save_cnt) - in[cnt--] = in_tmp[--save_cnt]; - return 0; -} - -void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, - char *buf) -{ - if (proto == QETH_PROT_IPV4) - qeth_l3_ipaddr4_to_string(addr, buf); - else if (proto == QETH_PROT_IPV6) - qeth_l3_ipaddr6_to_string(addr, buf); -} - -int qeth_l3_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto, - __u8 *addr) -{ - if (proto == QETH_PROT_IPV4) - return qeth_l3_string_to_ipaddr4(buf, addr); - else if (proto == QETH_PROT_IPV6) - return qeth_l3_string_to_ipaddr6(buf, addr); - else - return -EINVAL; + return NULL; } static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len) @@ -167,8 +84,8 @@ static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len) } } -int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, - struct qeth_ipaddr *addr) +static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, + struct qeth_ipaddr *addr) { struct qeth_ipato_entry *ipatoe; u8 addr_bits[128] = {0, }; @@ -176,22 +93,19 @@ int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, int rc = 0; if (!card->ipato.enabled) - return 0; + return false; + if (addr->type != QETH_IP_TYPE_NORMAL) + return false; qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits, - (addr->proto == QETH_PROT_IPV4)? 4:16); + (addr->proto == QETH_PROT_IPV4) ? 4 : 16); list_for_each_entry(ipatoe, &card->ipato.entries, entry) { if (addr->proto != ipatoe->proto) continue; qeth_l3_convert_addr_to_bits(ipatoe->addr, ipatoe_bits, (ipatoe->proto == QETH_PROT_IPV4) ? 4 : 16); - if (addr->proto == QETH_PROT_IPV4) - rc = !memcmp(addr_bits, ipatoe_bits, - min(32, ipatoe->mask_bits)); - else - rc = !memcmp(addr_bits, ipatoe_bits, - min(128, ipatoe->mask_bits)); + rc = !memcmp(addr_bits, ipatoe_bits, ipatoe->mask_bits); if (rc) break; } @@ -204,407 +118,282 @@ int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, return rc; } -/* - * Add IP to be added to todo list. If there is already an "add todo" - * in this list we just incremenent the reference count. - * Returns 0 if we just incremented reference count. - */ -static int __qeth_l3_insert_ip_todo(struct qeth_card *card, - struct qeth_ipaddr *addr, int add) -{ - struct qeth_ipaddr *tmp, *t; - int found = 0; - - if (card->options.sniffer) - return 0; - list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) { - if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) && - (tmp->type == QETH_IP_TYPE_DEL_ALL_MC)) - return 0; - if ((tmp->proto == QETH_PROT_IPV4) && - (addr->proto == QETH_PROT_IPV4) && - (tmp->type == addr->type) && - (tmp->is_multicast == addr->is_multicast) && - (tmp->u.a4.addr == addr->u.a4.addr) && - (tmp->u.a4.mask == addr->u.a4.mask)) { - found = 1; - break; - } - if ((tmp->proto == QETH_PROT_IPV6) && - (addr->proto == QETH_PROT_IPV6) && - (tmp->type == addr->type) && - (tmp->is_multicast == addr->is_multicast) && - (tmp->u.a6.pfxlen == addr->u.a6.pfxlen) && - (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr, - sizeof(struct in6_addr)) == 0)) { - found = 1; - break; - } - } - if (found) { - if (addr->users != 0) - tmp->users += addr->users; - else - tmp->users += add ? 1 : -1; - if (tmp->users == 0) { - list_del(&tmp->entry); - kfree(tmp); - } - return 0; - } else { - if (addr->type == QETH_IP_TYPE_DEL_ALL_MC) - list_add(&addr->entry, card->ip_tbd_list); - else { - if (addr->users == 0) - addr->users += add ? 1 : -1; - if (add && (addr->type == QETH_IP_TYPE_NORMAL) && - qeth_l3_is_addr_covered_by_ipato(card, addr)) { - QETH_CARD_TEXT(card, 2, "tkovaddr"); - addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG; - } - list_add_tail(&addr->entry, card->ip_tbd_list); - } - return 1; - } -} - -int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +static int qeth_l3_delete_ip(struct qeth_card *card, + struct qeth_ipaddr *tmp_addr) { - unsigned long flags; int rc = 0; + struct qeth_ipaddr *addr; - QETH_CARD_TEXT(card, 4, "delip"); + if (tmp_addr->type == QETH_IP_TYPE_RXIP) + QETH_CARD_TEXT(card, 2, "delrxip"); + else if (tmp_addr->type == QETH_IP_TYPE_VIPA) + QETH_CARD_TEXT(card, 2, "delvipa"); + else + QETH_CARD_TEXT(card, 2, "delip"); - if (addr->proto == QETH_PROT_IPV4) - QETH_CARD_HEX(card, 4, &addr->u.a4.addr, 4); + if (tmp_addr->proto == QETH_PROT_IPV4) + QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4); else { - QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8); - QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 8); + QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8); + QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); } - spin_lock_irqsave(&card->ip_lock, flags); - rc = __qeth_l3_insert_ip_todo(card, addr, 0); - spin_unlock_irqrestore(&card->ip_lock, flags); - return rc; -} -int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr) -{ - unsigned long flags; - int rc = 0; + addr = qeth_l3_find_addr_by_ip(card, tmp_addr); + if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr)) + return -ENOENT; - QETH_CARD_TEXT(card, 4, "addip"); - if (addr->proto == QETH_PROT_IPV4) - QETH_CARD_HEX(card, 4, &addr->u.a4.addr, 4); - else { - QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8); - QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 8); - } - spin_lock_irqsave(&card->ip_lock, flags); - rc = __qeth_l3_insert_ip_todo(card, addr, 1); - spin_unlock_irqrestore(&card->ip_lock, flags); - return rc; -} + addr->ref_counter--; + if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0) + return rc; + if (qeth_card_hw_is_reachable(card)) + rc = qeth_l3_deregister_addr_entry(card, addr); -struct qeth_ipaddr *qeth_l3_get_addr_buffer( - enum qeth_prot_versions prot) -{ - struct qeth_ipaddr *addr; + hash_del(&addr->hnode); + kfree(addr); - addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC); - if (addr == NULL) { - return NULL; - } - addr->type = QETH_IP_TYPE_NORMAL; - addr->proto = prot; - return addr; + return rc; } -static void qeth_l3_delete_mc_addresses(struct qeth_card *card) +static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) { - struct qeth_ipaddr *iptodo; - unsigned long flags; + int rc = 0; + struct qeth_ipaddr *addr; + char buf[INET6_ADDRSTRLEN]; - QETH_CARD_TEXT(card, 4, "delmc"); - iptodo = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!iptodo) { - QETH_CARD_TEXT(card, 2, "dmcnomem"); - return; + if (tmp_addr->type == QETH_IP_TYPE_RXIP) + QETH_CARD_TEXT(card, 2, "addrxip"); + else if (tmp_addr->type == QETH_IP_TYPE_VIPA) + QETH_CARD_TEXT(card, 2, "addvipa"); + else + QETH_CARD_TEXT(card, 2, "addip"); + + if (tmp_addr->proto == QETH_PROT_IPV4) + QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4); + else { + QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8); + QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); } - iptodo->type = QETH_IP_TYPE_DEL_ALL_MC; - spin_lock_irqsave(&card->ip_lock, flags); - if (!__qeth_l3_insert_ip_todo(card, iptodo, 0)) - kfree(iptodo); - spin_unlock_irqrestore(&card->ip_lock, flags); -} -/* - * Add/remove address to/from card's ip list, i.e. try to add or remove - * reference to/from an IP address that is already registered on the card. - * Returns: - * 0 address was on card and its reference count has been adjusted, - * but is still > 0, so nothing has to be done - * also returns 0 if card was not on card and the todo was to delete - * the address -> there is also nothing to be done - * 1 address was not on card and the todo is to add it to the card's ip - * list - * -1 address was on card and its reference count has been decremented - * to <= 0 by the todo -> address must be removed from card - */ -static int __qeth_l3_ref_ip_on_card(struct qeth_card *card, - struct qeth_ipaddr *todo, struct qeth_ipaddr **__addr) -{ - struct qeth_ipaddr *addr; - int found = 0; - - list_for_each_entry(addr, &card->ip_list, entry) { - if ((addr->proto == QETH_PROT_IPV4) && - (todo->proto == QETH_PROT_IPV4) && - (addr->type == todo->type) && - (addr->u.a4.addr == todo->u.a4.addr) && - (addr->u.a4.mask == todo->u.a4.mask)) { - found = 1; - break; + addr = qeth_l3_find_addr_by_ip(card, tmp_addr); + if (addr) { + if (tmp_addr->type != QETH_IP_TYPE_NORMAL) + return -EADDRINUSE; + if (qeth_l3_addr_match_all(addr, tmp_addr)) { + addr->ref_counter++; + return 0; } - if ((addr->proto == QETH_PROT_IPV6) && - (todo->proto == QETH_PROT_IPV6) && - (addr->type == todo->type) && - (addr->u.a6.pfxlen == todo->u.a6.pfxlen) && - (memcmp(&addr->u.a6.addr, &todo->u.a6.addr, - sizeof(struct in6_addr)) == 0)) { - found = 1; - break; + qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u, + buf); + dev_warn(&card->gdev->dev, + "Registering IP address %s failed\n", buf); + return -EADDRINUSE; + } else { + addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL); + if (!addr) + return -ENOMEM; + + if (qeth_l3_is_addr_covered_by_ipato(card, addr)) { + QETH_CARD_TEXT(card, 2, "tkovaddr"); + addr->ipato = 1; } - } - if (found) { - addr->users += todo->users; - if (addr->users <= 0) { - *__addr = addr; - return -1; - } else { - /* for VIPA and RXIP limit refcount to 1 */ - if (addr->type != QETH_IP_TYPE_NORMAL) - addr->users = 1; + hash_add(card->ip_htable, &addr->hnode, + qeth_l3_ipaddr_hash(addr)); + + if (!qeth_card_hw_is_reachable(card)) { + addr->disp_flag = QETH_DISP_ADDR_ADD; return 0; } - } - if (todo->users > 0) { - /* for VIPA and RXIP limit refcount to 1 */ - if (todo->type != QETH_IP_TYPE_NORMAL) - todo->users = 1; - return 1; - } else - return 0; -} -static void __qeth_l3_delete_all_mc(struct qeth_card *card, - unsigned long *flags) -{ - struct list_head fail_list; - struct qeth_ipaddr *addr, *tmp; - int rc; + rc = qeth_l3_register_addr_entry(card, addr); - INIT_LIST_HEAD(&fail_list); -again: - list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) { - if (addr->is_multicast) { - list_del(&addr->entry); - spin_unlock_irqrestore(&card->ip_lock, *flags); - rc = qeth_l3_deregister_addr_entry(card, addr); - spin_lock_irqsave(&card->ip_lock, *flags); - if (!rc || (rc == IPA_RC_MC_ADDR_NOT_FOUND)) - kfree(addr); - else - list_add_tail(&addr->entry, &fail_list); - goto again; + if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { + addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + } else { + hash_del(&addr->hnode); + kfree(addr); } } - list_splice(&fail_list, &card->ip_list); + return rc; } -void qeth_l3_set_ip_addr_list(struct qeth_card *card) +static int qeth_l3_modify_ip(struct qeth_card *card, struct qeth_ipaddr *addr, + bool add) { - struct list_head *tbd_list; - struct qeth_ipaddr *todo, *addr; - unsigned long flags; int rc; - QETH_CARD_TEXT(card, 2, "sdiplist"); - QETH_CARD_HEX(card, 2, &card, sizeof(void *)); + mutex_lock(&card->ip_lock); + rc = add ? qeth_l3_add_ip(card, addr) : qeth_l3_delete_ip(card, addr); + mutex_unlock(&card->ip_lock); - if ((card->state != CARD_STATE_UP && - card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) { - return; - } + return rc; +} - spin_lock_irqsave(&card->ip_lock, flags); - tbd_list = card->ip_tbd_list; - card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC); - if (!card->ip_tbd_list) { - QETH_CARD_TEXT(card, 0, "silnomem"); - card->ip_tbd_list = tbd_list; - spin_unlock_irqrestore(&card->ip_lock, flags); - return; - } else - INIT_LIST_HEAD(card->ip_tbd_list); - - while (!list_empty(tbd_list)) { - todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry); - list_del(&todo->entry); - if (todo->type == QETH_IP_TYPE_DEL_ALL_MC) { - __qeth_l3_delete_all_mc(card, &flags); - kfree(todo); - continue; - } - rc = __qeth_l3_ref_ip_on_card(card, todo, &addr); - if (rc == 0) { - /* nothing to be done; only adjusted refcount */ - kfree(todo); - } else if (rc == 1) { - /* new entry to be added to on-card list */ - spin_unlock_irqrestore(&card->ip_lock, flags); - rc = qeth_l3_register_addr_entry(card, todo); - spin_lock_irqsave(&card->ip_lock, flags); - if (!rc || (rc == IPA_RC_LAN_OFFLINE)) - list_add_tail(&todo->entry, &card->ip_list); - else - kfree(todo); - } else if (rc == -1) { - /* on-card entry to be removed */ - list_del_init(&addr->entry); - spin_unlock_irqrestore(&card->ip_lock, flags); - rc = qeth_l3_deregister_addr_entry(card, addr); - spin_lock_irqsave(&card->ip_lock, flags); - if (!rc || (rc == IPA_RC_IP_ADDRESS_NOT_DEFINED)) - kfree(addr); - else - list_add_tail(&addr->entry, &card->ip_list); - kfree(todo); - } +static void qeth_l3_drain_rx_mode_cache(struct qeth_card *card) +{ + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i; + + hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) { + hash_del(&addr->hnode); + kfree(addr); } - spin_unlock_irqrestore(&card->ip_lock, flags); - kfree(tbd_list); } -static void qeth_l3_clear_ip_list(struct qeth_card *card, int recover) +static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover) { - struct qeth_ipaddr *addr, *tmp; - unsigned long flags; + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i; QETH_CARD_TEXT(card, 4, "clearip"); - if (recover && card->options.sniffer) - return; - spin_lock_irqsave(&card->ip_lock, flags); - /* clear todo list */ - list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry) { - list_del(&addr->entry); - kfree(addr); - } - while (!list_empty(&card->ip_list)) { - addr = list_entry(card->ip_list.next, - struct qeth_ipaddr, entry); - list_del_init(&addr->entry); - if (!recover || addr->is_multicast) { + mutex_lock(&card->ip_lock); + + hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { + if (!recover) { + hash_del(&addr->hnode); kfree(addr); - continue; + } else { + /* prepare for recovery */ + addr->disp_flag = QETH_DISP_ADDR_ADD; } - list_add_tail(&addr->entry, card->ip_tbd_list); } - spin_unlock_irqrestore(&card->ip_lock, flags); + + mutex_unlock(&card->ip_lock); } -static int qeth_l3_address_exists_in_list(struct list_head *list, - struct qeth_ipaddr *addr, int same_type) +static void qeth_l3_recover_ip(struct qeth_card *card) { - struct qeth_ipaddr *tmp; + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i; + int rc; + + QETH_CARD_TEXT(card, 4, "recovrip"); - list_for_each_entry(tmp, list, entry) { - if ((tmp->proto == QETH_PROT_IPV4) && - (addr->proto == QETH_PROT_IPV4) && - ((same_type && (tmp->type == addr->type)) || - (!same_type && (tmp->type != addr->type))) && - (tmp->u.a4.addr == addr->u.a4.addr)) - return 1; + mutex_lock(&card->ip_lock); - if ((tmp->proto == QETH_PROT_IPV6) && - (addr->proto == QETH_PROT_IPV6) && - ((same_type && (tmp->type == addr->type)) || - (!same_type && (tmp->type != addr->type))) && - (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr, - sizeof(struct in6_addr)) == 0)) - return 1; + hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { + if (addr->disp_flag == QETH_DISP_ADDR_ADD) { + rc = qeth_l3_register_addr_entry(card, addr); + if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { + /* keep it in the records */ + addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + } else { + /* bad address */ + hash_del(&addr->hnode); + kfree(addr); + } + } + } + + mutex_unlock(&card->ip_lock); +} + +static int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; + + switch (cmd->hdr.return_code) { + case IPA_RC_SUCCESS: + return 0; + case IPA_RC_DUPLICATE_IP_ADDRESS: + return -EADDRINUSE; + case IPA_RC_MC_ADDR_NOT_FOUND: + return -ENOENT; + case IPA_RC_LAN_OFFLINE: + return -ENETDOWN; + default: + return -EIO; } - return 0; } static int qeth_l3_send_setdelmc(struct qeth_card *card, - struct qeth_ipaddr *addr, int ipacmd) + struct qeth_ipaddr *addr, + enum qeth_ipa_cmds ipacmd) { - int rc; struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; QETH_CARD_TEXT(card, 4, "setdelmc"); - iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - memcpy(&cmd->data.setdelipm.mac, addr->mac, OSA_ADDR_LEN); - if (addr->proto == QETH_PROT_IPV6) - memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr, - sizeof(struct in6_addr)); - else - memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr, 4); + iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto, + IPA_DATA_SIZEOF(setdelipm)); + if (!iob) + return -ENOMEM; + cmd = __ipa_cmd(iob); + if (addr->proto == QETH_PROT_IPV6) { + cmd->data.setdelipm.ip = addr->u.a6.addr; + ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac); + } else { + cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr; + ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac); + } - rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); + return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); +} - return rc; +static void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len) +{ + unsigned int i = 0; + + while (len && i < 4) { + int mask_len = min_t(int, len, 32); + + prefix->s6_addr32[i] = inet_make_mask(mask_len); + len -= mask_len; + i++; + } } -static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len) +static u32 qeth_l3_get_setdelip_flags(struct qeth_ipaddr *addr, bool set) { - int i, j; - for (i = 0; i < 16; i++) { - j = (len) - (i * 8); - if (j >= 8) - netmask[i] = 0xff; - else if (j > 0) - netmask[i] = (u8)(0xFF00 >> j); - else - netmask[i] = 0; + switch (addr->type) { + case QETH_IP_TYPE_RXIP: + return (set) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0; + case QETH_IP_TYPE_VIPA: + return (set) ? QETH_IPA_SETIP_VIPA_FLAG : + QETH_IPA_DELIP_VIPA_FLAG; + default: + return (set && addr->ipato) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0; } } static int qeth_l3_send_setdelip(struct qeth_card *card, - struct qeth_ipaddr *addr, int ipacmd, unsigned int flags) + struct qeth_ipaddr *addr, + enum qeth_ipa_cmds ipacmd) { - int rc; struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - __u8 netmask[16]; + u32 flags; QETH_CARD_TEXT(card, 4, "setdelip"); + + iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto, + IPA_DATA_SIZEOF(setdelip6)); + if (!iob) + return -ENOMEM; + cmd = __ipa_cmd(iob); + + flags = qeth_l3_get_setdelip_flags(addr, ipacmd == IPA_CMD_SETIP); QETH_CARD_TEXT_(card, 4, "flags%02X", flags); - iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); if (addr->proto == QETH_PROT_IPV6) { - memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr, - sizeof(struct in6_addr)); - qeth_l3_fill_netmask(netmask, addr->u.a6.pfxlen); - memcpy(cmd->data.setdelip6.mask, netmask, - sizeof(struct in6_addr)); + cmd->data.setdelip6.addr = addr->u.a6.addr; + qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix, + addr->u.a6.pfxlen); cmd->data.setdelip6.flags = flags; } else { - memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4); - memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4); + cmd->data.setdelip4.addr = addr->u.a4.addr; + cmd->data.setdelip4.mask = addr->u.a4.mask; cmd->data.setdelip4.flags = flags; } - rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); - - return rc; + return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); } static int qeth_l3_send_setrouting(struct qeth_card *card, @@ -615,8 +404,11 @@ static int qeth_l3_send_setrouting(struct qeth_card *card, struct qeth_cmd_buffer *iob; QETH_CARD_TEXT(card, 4, "setroutg"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + iob = qeth_ipa_alloc_cmd(card, IPA_CMD_SETRTG, prot, + IPA_DATA_SIZEOF(setrtg)); + if (!iob) + return -ENOMEM; + cmd = __ipa_cmd(iob); cmd->data.setrtg.type = (type); rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); @@ -626,7 +418,7 @@ static int qeth_l3_send_setrouting(struct qeth_card *card, static int qeth_l3_correct_routing_type(struct qeth_card *card, enum qeth_routing_types *type, enum qeth_prot_versions prot) { - if (card->info.type == QETH_CARD_TYPE_IQD) { + if (IS_IQD(card)) { switch (*type) { case NO_ROUTER: case PRIMARY_CONNECTOR: @@ -646,6 +438,7 @@ static int qeth_l3_correct_routing_type(struct qeth_card *card, if (qeth_is_ipafunc_supported(card, prot, IPA_OSA_MC_ROUTER)) return 0; + goto out_inval; default: goto out_inval; } @@ -670,9 +463,8 @@ int qeth_l3_setrouting_v4(struct qeth_card *card) QETH_PROT_IPV4); if (rc) { card->options.route4.type = NO_ROUTER; - QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type" - " on %s. Type set to 'no router'.\n", rc, - QETH_CARD_IFNAME(card)); + QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n", + rc, CARD_DEVID(card)); } return rc; } @@ -682,7 +474,6 @@ int qeth_l3_setrouting_v6(struct qeth_card *card) int rc = 0; QETH_CARD_TEXT(card, 3, "setrtg6"); -#ifdef CONFIG_QETH_IPV6 if (!qeth_is_supported(card, IPA_IPV6)) return 0; @@ -695,206 +486,135 @@ int qeth_l3_setrouting_v6(struct qeth_card *card) QETH_PROT_IPV6); if (rc) { card->options.route6.type = NO_ROUTER; - QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type" - " on %s. Type set to 'no router'.\n", rc, - QETH_CARD_IFNAME(card)); + QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n", + rc, CARD_DEVID(card)); } -#endif return rc; } /* * IP address takeover related functions */ -static void qeth_l3_clear_ipato_list(struct qeth_card *card) + +/* + * qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs. + * + * Caller must hold ip_lock. + */ +void qeth_l3_update_ipato(struct qeth_card *card) { + struct qeth_ipaddr *addr; + unsigned int i; + + hash_for_each(card->ip_htable, i, addr, hnode) { + if (addr->type != QETH_IP_TYPE_NORMAL) + continue; + addr->ipato = qeth_l3_is_addr_covered_by_ipato(card, addr); + } +} +static void qeth_l3_clear_ipato_list(struct qeth_card *card) +{ struct qeth_ipato_entry *ipatoe, *tmp; - unsigned long flags; - spin_lock_irqsave(&card->ip_lock, flags); + mutex_lock(&card->ip_lock); + list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { list_del(&ipatoe->entry); kfree(ipatoe); } - spin_unlock_irqrestore(&card->ip_lock, flags); + + qeth_l3_update_ipato(card); + mutex_unlock(&card->ip_lock); } int qeth_l3_add_ipato_entry(struct qeth_card *card, struct qeth_ipato_entry *new) { struct qeth_ipato_entry *ipatoe; - unsigned long flags; int rc = 0; QETH_CARD_TEXT(card, 2, "addipato"); - spin_lock_irqsave(&card->ip_lock, flags); + + mutex_lock(&card->ip_lock); + list_for_each_entry(ipatoe, &card->ipato.entries, entry) { if (ipatoe->proto != new->proto) continue; if (!memcmp(ipatoe->addr, new->addr, - (ipatoe->proto == QETH_PROT_IPV4)? 4:16) && + (ipatoe->proto == QETH_PROT_IPV4) ? 4 : 16) && (ipatoe->mask_bits == new->mask_bits)) { rc = -EEXIST; break; } } - if (!rc) + + if (!rc) { list_add_tail(&new->entry, &card->ipato.entries); + qeth_l3_update_ipato(card); + } + + mutex_unlock(&card->ip_lock); - spin_unlock_irqrestore(&card->ip_lock, flags); return rc; } -void qeth_l3_del_ipato_entry(struct qeth_card *card, - enum qeth_prot_versions proto, u8 *addr, int mask_bits) +int qeth_l3_del_ipato_entry(struct qeth_card *card, + enum qeth_prot_versions proto, u8 *addr, + unsigned int mask_bits) { struct qeth_ipato_entry *ipatoe, *tmp; - unsigned long flags; + int rc = -ENOENT; QETH_CARD_TEXT(card, 2, "delipato"); - spin_lock_irqsave(&card->ip_lock, flags); + + mutex_lock(&card->ip_lock); + list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { if (ipatoe->proto != proto) continue; if (!memcmp(ipatoe->addr, addr, - (proto == QETH_PROT_IPV4)? 4:16) && + (proto == QETH_PROT_IPV4) ? 4 : 16) && (ipatoe->mask_bits == mask_bits)) { list_del(&ipatoe->entry); + qeth_l3_update_ipato(card); kfree(ipatoe); + rc = 0; } } - spin_unlock_irqrestore(&card->ip_lock, flags); -} -/* - * VIPA related functions - */ -int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto, - const u8 *addr) -{ - struct qeth_ipaddr *ipaddr; - unsigned long flags; - int rc = 0; + mutex_unlock(&card->ip_lock); - ipaddr = qeth_l3_get_addr_buffer(proto); - if (ipaddr) { - if (proto == QETH_PROT_IPV4) { - QETH_CARD_TEXT(card, 2, "addvipa4"); - memcpy(&ipaddr->u.a4.addr, addr, 4); - ipaddr->u.a4.mask = 0; - } else if (proto == QETH_PROT_IPV6) { - QETH_CARD_TEXT(card, 2, "addvipa6"); - memcpy(&ipaddr->u.a6.addr, addr, 16); - ipaddr->u.a6.pfxlen = 0; - } - ipaddr->type = QETH_IP_TYPE_VIPA; - ipaddr->set_flags = QETH_IPA_SETIP_VIPA_FLAG; - ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG; - } else - return -ENOMEM; - spin_lock_irqsave(&card->ip_lock, flags); - if (qeth_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) || - qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0)) - rc = -EEXIST; - spin_unlock_irqrestore(&card->ip_lock, flags); - if (rc) { - kfree(ipaddr); - return rc; - } - if (!qeth_l3_add_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); return rc; } -void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto, - const u8 *addr) +int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, + enum qeth_ip_types type, + enum qeth_prot_versions proto) { - struct qeth_ipaddr *ipaddr; + struct qeth_ipaddr addr; - ipaddr = qeth_l3_get_addr_buffer(proto); - if (ipaddr) { - if (proto == QETH_PROT_IPV4) { - QETH_CARD_TEXT(card, 2, "delvipa4"); - memcpy(&ipaddr->u.a4.addr, addr, 4); - ipaddr->u.a4.mask = 0; - } else if (proto == QETH_PROT_IPV6) { - QETH_CARD_TEXT(card, 2, "delvipa6"); - memcpy(&ipaddr->u.a6.addr, addr, 16); - ipaddr->u.a6.pfxlen = 0; - } - ipaddr->type = QETH_IP_TYPE_VIPA; - } else - return; - if (!qeth_l3_delete_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); -} - -/* - * proxy ARP related functions - */ -int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto, - const u8 *addr) -{ - struct qeth_ipaddr *ipaddr; - unsigned long flags; - int rc = 0; + qeth_l3_init_ipaddr(&addr, type, proto); + if (proto == QETH_PROT_IPV4) + memcpy(&addr.u.a4.addr, ip, 4); + else + memcpy(&addr.u.a6.addr, ip, 16); - ipaddr = qeth_l3_get_addr_buffer(proto); - if (ipaddr) { - if (proto == QETH_PROT_IPV4) { - QETH_CARD_TEXT(card, 2, "addrxip4"); - memcpy(&ipaddr->u.a4.addr, addr, 4); - ipaddr->u.a4.mask = 0; - } else if (proto == QETH_PROT_IPV6) { - QETH_CARD_TEXT(card, 2, "addrxip6"); - memcpy(&ipaddr->u.a6.addr, addr, 16); - ipaddr->u.a6.pfxlen = 0; - } - ipaddr->type = QETH_IP_TYPE_RXIP; - ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG; - ipaddr->del_flags = 0; - } else - return -ENOMEM; - spin_lock_irqsave(&card->ip_lock, flags); - if (qeth_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) || - qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0)) - rc = -EEXIST; - spin_unlock_irqrestore(&card->ip_lock, flags); - if (rc) { - kfree(ipaddr); - return rc; - } - if (!qeth_l3_add_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); - return 0; + return qeth_l3_modify_ip(card, &addr, add); } -void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto, - const u8 *addr) +int qeth_l3_modify_hsuid(struct qeth_card *card, bool add) { - struct qeth_ipaddr *ipaddr; + struct qeth_ipaddr addr; + unsigned int i; - ipaddr = qeth_l3_get_addr_buffer(proto); - if (ipaddr) { - if (proto == QETH_PROT_IPV4) { - QETH_CARD_TEXT(card, 2, "addrxip4"); - memcpy(&ipaddr->u.a4.addr, addr, 4); - ipaddr->u.a4.mask = 0; - } else if (proto == QETH_PROT_IPV6) { - QETH_CARD_TEXT(card, 2, "addrxip6"); - memcpy(&ipaddr->u.a6.addr, addr, 16); - ipaddr->u.a6.pfxlen = 0; - } - ipaddr->type = QETH_IP_TYPE_RXIP; - } else - return; - if (!qeth_l3_delete_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); + qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); + addr.u.a6.addr.s6_addr[0] = 0xfe; + addr.u.a6.addr.s6_addr[1] = 0x80; + for (i = 0; i < 8; i++) + addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i]; + + return qeth_l3_modify_ip(card, &addr, add); } static int qeth_l3_register_addr_entry(struct qeth_card *card, @@ -904,6 +624,9 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card, int rc = 0; int cnt = 3; + if (card->options.sniffer) + return 0; + if (addr->proto == QETH_PROT_IPV4) { QETH_CARD_TEXT(card, 2, "setaddr4"); QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); @@ -919,8 +642,7 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card, if (addr->is_multicast) rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM); else - rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP, - addr->set_flags); + rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP); if (rc) QETH_CARD_TEXT(card, 2, "failed"); } while ((--cnt > 0) && rc); @@ -938,6 +660,9 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *card, { int rc = 0; + if (card->options.sniffer) + return 0; + if (addr->proto == QETH_PROT_IPV4) { QETH_CARD_TEXT(card, 2, "deladdr4"); QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); @@ -952,53 +677,19 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *card, if (addr->is_multicast) rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM); else - rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP, - addr->del_flags); + rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP); if (rc) QETH_CARD_TEXT(card, 2, "failed"); return rc; } -static inline u8 qeth_l3_get_qeth_hdr_flags4(int cast_type) -{ - if (cast_type == RTN_MULTICAST) - return QETH_CAST_MULTICAST; - if (cast_type == RTN_BROADCAST) - return QETH_CAST_BROADCAST; - return QETH_CAST_UNICAST; -} - -static inline u8 qeth_l3_get_qeth_hdr_flags6(int cast_type) -{ - u8 ct = QETH_HDR_PASSTHRU | QETH_HDR_IPV6; - if (cast_type == RTN_MULTICAST) - return ct | QETH_CAST_MULTICAST; - if (cast_type == RTN_ANYCAST) - return ct | QETH_CAST_ANYCAST; - if (cast_type == RTN_BROADCAST) - return ct | QETH_CAST_BROADCAST; - return ct | QETH_CAST_UNICAST; -} - static int qeth_l3_setadapter_parms(struct qeth_card *card) { - int rc; + int rc = 0; - QETH_DBF_TEXT(SETUP, 2, "setadprm"); + QETH_CARD_TEXT(card, 2, "setadprm"); - if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)) { - dev_info(&card->gdev->dev, - "set adapter parameters not supported.\n"); - QETH_DBF_TEXT(SETUP, 2, " notsupp"); - return 0; - } - rc = qeth_query_setadapterparms(card); - if (rc) { - QETH_DBF_MESSAGE(2, "%s couldn't set adapter parameters: " - "0x%x\n", dev_name(&card->gdev->dev), rc); - return rc; - } if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) { rc = qeth_setadpparms_change_macaddr(card); if (rc) @@ -1009,110 +700,6 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card) return rc; } -static int qeth_l3_default_setassparms_cb(struct qeth_card *card, - struct qeth_reply *reply, unsigned long data) -{ - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "defadpcb"); - - cmd = (struct qeth_ipa_cmd *) data; - if (cmd->hdr.return_code == 0) { - cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; - if (cmd->hdr.prot_version == QETH_PROT_IPV4) - card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; - if (cmd->hdr.prot_version == QETH_PROT_IPV6) - card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; - } - if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM && - cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { - card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; - QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask); - } - if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM && - cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { - card->info.tx_csum_mask = - cmd->data.setassparms.data.flags_32bit; - QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask); - } - - return 0; -} - -static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd( - struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code, - __u16 len, enum qeth_prot_versions prot) -{ - struct qeth_cmd_buffer *iob; - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "getasscm"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); - - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.setassparms.hdr.assist_no = ipa_func; - cmd->data.setassparms.hdr.length = 8 + len; - cmd->data.setassparms.hdr.command_code = cmd_code; - cmd->data.setassparms.hdr.return_code = 0; - cmd->data.setassparms.hdr.seq_no = 0; - - return iob; -} - -static int qeth_l3_send_setassparms(struct qeth_card *card, - struct qeth_cmd_buffer *iob, __u16 len, long data, - int (*reply_cb)(struct qeth_card *, struct qeth_reply *, - unsigned long), - void *reply_param) -{ - int rc; - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "sendassp"); - - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - if (len <= sizeof(__u32)) - cmd->data.setassparms.data.flags_32bit = (__u32) data; - else /* (len > sizeof(__u32)) */ - memcpy(&cmd->data.setassparms.data, (void *) data, len); - - rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); - return rc; -} - -#ifdef CONFIG_QETH_IPV6 -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_l3_get_setassparms_cmd(card, ipa_func, cmd_code, - 0, QETH_PROT_IPV6); - rc = qeth_l3_send_setassparms(card, iob, 0, 0, - qeth_l3_default_setassparms_cb, NULL); - return rc; -} -#endif - -static int qeth_l3_send_simple_setassparms(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data) -{ - int rc; - int length = 0; - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 4, "simassp4"); - if (data) - length = sizeof(__u32); - iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code, - length, QETH_PROT_IPV4); - rc = qeth_l3_send_setassparms(card, iob, length, data, - qeth_l3_default_setassparms_cb, NULL); - return rc; -} - static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) { int rc; @@ -1121,45 +708,20 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { dev_info(&card->gdev->dev, - "ARP processing not supported on %s!\n", - QETH_CARD_IFNAME(card)); + "ARP processing not supported on %s!\n", + netdev_name(card->dev)); return 0; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_START, NULL); if (rc) { dev_warn(&card->gdev->dev, - "Starting ARP processing support for %s failed\n", - QETH_CARD_IFNAME(card)); + "Starting ARP processing support for %s failed\n", + netdev_name(card->dev)); } return rc; } -static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card) -{ - int rc; - - QETH_CARD_TEXT(card, 3, "ipaipfrg"); - - if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) { - dev_info(&card->gdev->dev, - "Hardware IP fragmentation not supported on %s\n", - QETH_CARD_IFNAME(card)); - return -EOPNOTSUPP; - } - - rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, - IPA_CMD_ASS_START, 0); - if (rc) { - dev_warn(&card->gdev->dev, - "Starting IP fragmentation support for %s failed\n", - QETH_CARD_IFNAME(card)); - } else - dev_info(&card->gdev->dev, - "Hardware IP fragmentation enabled \n"); - return rc; -} - static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) { int rc; @@ -1168,17 +730,17 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) if (!qeth_is_supported(card, IPA_SOURCE_MAC)) { dev_info(&card->gdev->dev, - "Inbound source MAC-address not supported on %s\n", - QETH_CARD_IFNAME(card)); + "Inbound source MAC-address not supported on %s\n", + netdev_name(card->dev)); return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC, + IPA_CMD_ASS_START, NULL); if (rc) dev_warn(&card->gdev->dev, - "Starting source MAC-address support for %s failed\n", - QETH_CARD_IFNAME(card)); + "Starting source MAC-address support for %s failed\n", + netdev_name(card->dev)); return rc; } @@ -1190,16 +752,16 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card) if (!qeth_is_supported(card, IPA_FULL_VLAN)) { dev_info(&card->gdev->dev, - "VLAN not supported on %s\n", QETH_CARD_IFNAME(card)); + "VLAN not supported on %s\n", netdev_name(card->dev)); return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO, + IPA_CMD_ASS_START, NULL); if (rc) { dev_warn(&card->gdev->dev, - "Starting VLAN support for %s failed\n", - QETH_CARD_IFNAME(card)); + "Starting VLAN support for %s failed\n", + netdev_name(card->dev)); } else { dev_info(&card->gdev->dev, "VLAN enabled\n"); } @@ -1214,17 +776,17 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card) if (!qeth_is_supported(card, IPA_MULTICASTING)) { dev_info(&card->gdev->dev, - "Multicast not supported on %s\n", - QETH_CARD_IFNAME(card)); + "Multicast not supported on %s\n", + netdev_name(card->dev)); return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING, + IPA_CMD_ASS_START, NULL); if (rc) { dev_warn(&card->gdev->dev, - "Starting multicast support for %s failed\n", - QETH_CARD_IFNAME(card)); + "Starting multicast support for %s failed\n", + netdev_name(card->dev)); } else { dev_info(&card->gdev->dev, "Multicast enabled\n"); card->dev->flags |= IFF_MULTICAST; @@ -1232,106 +794,96 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card) return rc; } -#ifdef CONFIG_QETH_IPV6 static int qeth_l3_softsetup_ipv6(struct qeth_card *card) { + u32 ipv6_data = 3; int rc; QETH_CARD_TEXT(card, 3, "softipv6"); - if (card->info.type == QETH_CARD_TYPE_IQD) + if (IS_IQD(card)) goto out; - 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; - } - rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6, - IPA_CMD_ASS_START, 3); + rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START, + &ipv6_data); if (rc) { dev_err(&card->gdev->dev, "Activating IPv6 support for %s failed\n", - QETH_CARD_IFNAME(card)); + netdev_name(card->dev)); 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, + NULL); if (rc) { dev_err(&card->gdev->dev, "Activating IPv6 support for %s failed\n", - QETH_CARD_IFNAME(card)); + netdev_name(card->dev)); 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, NULL); if (rc) { dev_warn(&card->gdev->dev, - "Enabling the passthrough mode for %s failed\n", - QETH_CARD_IFNAME(card)); + "Enabling the passthrough mode for %s failed\n", + netdev_name(card->dev)); return rc; } out: dev_info(&card->gdev->dev, "IPV6 enabled\n"); return 0; } -#endif static int qeth_l3_start_ipa_ipv6(struct qeth_card *card) { - int rc = 0; - QETH_CARD_TEXT(card, 3, "strtipv6"); if (!qeth_is_supported(card, IPA_IPV6)) { dev_info(&card->gdev->dev, - "IPv6 not supported on %s\n", QETH_CARD_IFNAME(card)); + "IPv6 not supported on %s\n", netdev_name(card->dev)); return 0; } -#ifdef CONFIG_QETH_IPV6 - rc = qeth_l3_softsetup_ipv6(card); -#endif - return rc ; + return qeth_l3_softsetup_ipv6(card); } static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) { + u32 filter_data = 1; int rc; QETH_CARD_TEXT(card, 3, "stbrdcst"); card->info.broadcast_capable = 0; if (!qeth_is_supported(card, IPA_FILTERING)) { dev_info(&card->gdev->dev, - "Broadcast not supported on %s\n", - QETH_CARD_IFNAME(card)); + "Broadcast not supported on %s\n", + netdev_name(card->dev)); rc = -EOPNOTSUPP; goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_START, NULL); if (rc) { - dev_warn(&card->gdev->dev, "Enabling broadcast filtering for " - "%s failed\n", QETH_CARD_IFNAME(card)); + dev_warn(&card->gdev->dev, + "Enabling broadcast filtering for %s failed\n", + netdev_name(card->dev)); goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, - IPA_CMD_ASS_CONFIGURE, 1); + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_CONFIGURE, &filter_data); if (rc) { dev_warn(&card->gdev->dev, - "Setting up broadcast filtering for %s failed\n", - QETH_CARD_IFNAME(card)); + "Setting up broadcast filtering for %s failed\n", + netdev_name(card->dev)); goto out; } card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; dev_info(&card->gdev->dev, "Broadcast enabled\n"); - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, - IPA_CMD_ASS_ENABLE, 1); + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_ENABLE, &filter_data); if (rc) { - dev_warn(&card->gdev->dev, "Setting up broadcast echo " - "filtering for %s failed\n", QETH_CARD_IFNAME(card)); + dev_warn(&card->gdev->dev, + "Setting up broadcast echo filtering for %s failed\n", + netdev_name(card->dev)); goto out; } card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO; @@ -1343,144 +895,29 @@ out: return rc; } -static int qeth_l3_send_checksum_command(struct qeth_card *card) -{ - int rc; - - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) { - dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, - card->info.csum_mask); - if (rc) { - dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - return 0; -} - -static int qeth_l3_set_rx_csum(struct qeth_card *card, int on) -{ - int rc = 0; - - if (on) { - rc = qeth_l3_send_checksum_command(card); - if (rc) - return -EIO; - dev_info(&card->gdev->dev, - "HW Checksumming (inbound) enabled\n"); - } else { - rc = qeth_l3_send_simple_setassparms(card, - IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); - if (rc) - return -EIO; - } - - return 0; -} - -static int qeth_l3_start_ipa_checksum(struct qeth_card *card) -{ - QETH_CARD_TEXT(card, 3, "strtcsum"); - - if (card->dev->features & NETIF_F_RXCSUM) { - rtnl_lock(); - /* force set_features call */ - card->dev->features &= ~NETIF_F_RXCSUM; - netdev_update_features(card->dev); - rtnl_unlock(); - } - return 0; -} - -static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) -{ - int rc = 0; - - if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) - return rc; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) - goto err_out; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask); - if (rc) - goto err_out; - dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); - return rc; -err_out: - dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " - "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); - return rc; -} - -static int qeth_l3_start_ipa_tso(struct qeth_card *card) -{ - int rc; - - QETH_CARD_TEXT(card, 3, "sttso"); - - if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) { - dev_info(&card->gdev->dev, - "Outbound TSO not supported on %s\n", - QETH_CARD_IFNAME(card)); - rc = -EOPNOTSUPP; - } else { - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO, - IPA_CMD_ASS_START, 0); - if (rc) - dev_warn(&card->gdev->dev, "Starting outbound TCP " - "segmentation offload for %s failed\n", - QETH_CARD_IFNAME(card)); - else - dev_info(&card->gdev->dev, - "Outbound TSO enabled\n"); - } - if (rc) - card->dev->features &= ~NETIF_F_TSO; - return rc; -} - -static int qeth_l3_start_ipassists(struct qeth_card *card) +static void qeth_l3_start_ipassists(struct qeth_card *card) { QETH_CARD_TEXT(card, 3, "strtipas"); - if (qeth_set_access_ctrl_online(card, 0)) - return -EIO; qeth_l3_start_ipa_arp_processing(card); /* go on*/ - qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/ qeth_l3_start_ipa_source_mac(card); /* go on*/ qeth_l3_start_ipa_vlan(card); /* go on*/ qeth_l3_start_ipa_multicast(card); /* go on*/ qeth_l3_start_ipa_ipv6(card); /* go on*/ qeth_l3_start_ipa_broadcast(card); /* go on*/ - qeth_l3_start_ipa_checksum(card); /* go on*/ - qeth_l3_start_ipa_tx_checksum(card); - qeth_l3_start_ipa_tso(card); /* go on*/ - return 0; } static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { - struct qeth_ipa_cmd *cmd; + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; - cmd = (struct qeth_ipa_cmd *) data; - if (cmd->hdr.return_code == 0) - memcpy(card->dev->dev_addr, - cmd->data.create_destroy_addr.unique_id, ETH_ALEN); - else - eth_random_addr(card->dev->dev_addr); + if (cmd->hdr.return_code) + return -EIO; + if (!is_valid_ether_addr(cmd->data.create_destroy_addr.mac_addr)) + return -EADDRNOTAVAIL; + eth_hw_addr_set(card->dev, cmd->data.create_destroy_addr.mac_addr); return 0; } @@ -1488,15 +925,13 @@ static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card) { int rc = 0; struct qeth_cmd_buffer *iob; - struct qeth_ipa_cmd *cmd; - QETH_DBF_TEXT(SETUP, 2, "hsrmac"); + QETH_CARD_TEXT(card, 2, "hsrmac"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR, - QETH_PROT_IPV6); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = - card->info.unique_id; + iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6, + IPA_DATA_SIZEOF(create_destroy_addr)); + if (!iob) + return -ENOMEM; rc = qeth_send_ipa_cmd(card, iob, qeth_l3_iqd_read_initial_mac_cb, NULL); @@ -1506,43 +941,37 @@ static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card) static int qeth_l3_get_unique_id_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { - struct qeth_ipa_cmd *cmd; + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; + u16 *uid = reply->param; - cmd = (struct qeth_ipa_cmd *) data; - if (cmd->hdr.return_code == 0) - card->info.unique_id = *((__u16 *) - &cmd->data.create_destroy_addr.unique_id[6]); - else { - card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | - UNIQUE_ID_NOT_BY_CARD; - dev_warn(&card->gdev->dev, "The network adapter failed to " - "generate a unique ID\n"); + if (cmd->hdr.return_code == 0) { + *uid = cmd->data.create_destroy_addr.uid; + return 0; } - return 0; + + dev_warn(&card->gdev->dev, "The network adapter failed to generate a unique ID\n"); + return -EIO; } -static int qeth_l3_get_unique_id(struct qeth_card *card) +static u16 qeth_l3_get_unique_id(struct qeth_card *card, u16 uid) { - int rc = 0; struct qeth_cmd_buffer *iob; - struct qeth_ipa_cmd *cmd; - QETH_DBF_TEXT(SETUP, 2, "guniqeid"); + QETH_CARD_TEXT(card, 2, "guniqeid"); - if (!qeth_is_supported(card, IPA_IPV6)) { - card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | - UNIQUE_ID_NOT_BY_CARD; - return 0; - } + if (!qeth_is_supported(card, IPA_IPV6)) + goto out; + + iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6, + IPA_DATA_SIZEOF(create_destroy_addr)); + if (!iob) + goto out; - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR, - QETH_PROT_IPV6); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = - card->info.unique_id; + __ipa_cmd(iob)->data.create_destroy_addr.uid = uid; + qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, &uid); - rc = qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, NULL); - return rc; +out: + return uid; } static int @@ -1552,7 +981,7 @@ qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply, struct qeth_ipa_cmd *cmd; __u16 rc; - QETH_DBF_TEXT(SETUP, 2, "diastrcb"); + QETH_CARD_TEXT(card, 2, "diastrcb"); cmd = (struct qeth_ipa_cmd *)data; rc = cmd->hdr.return_code; @@ -1595,11 +1024,11 @@ qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply, } break; default: - QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n", - cmd->data.diagass.action, QETH_CARD_IFNAME(card)); + QETH_DBF_MESSAGE(2, "Unknown sniffer action (%#06x) on device %x\n", + cmd->data.diagass.action, CARD_DEVID(card)); } - return 0; + return rc ? -EIO : 0; } static int @@ -1608,602 +1037,109 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - QETH_DBF_TEXT(SETUP, 2, "diagtrac"); + QETH_CARD_TEXT(card, 2, "diagtrac"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.diagass.subcmd_len = 16; - cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE; + iob = qeth_get_diag_cmd(card, QETH_DIAGS_CMD_TRACE, 0); + if (!iob) + return -ENOMEM; + cmd = __ipa_cmd(iob); cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET; cmd->data.diagass.action = diags_cmd; return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); } -static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac, - struct net_device *dev) -{ - ip_eth_mc_map(ipm, mac); -} - -static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) +static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) { + struct qeth_card *card = arg; + struct inet6_dev *in6_dev; + struct in_device *in4_dev; struct qeth_ipaddr *ipm; + struct qeth_ipaddr tmp; struct ip_mc_list *im4; - char buf[MAX_ADDR_LEN]; + struct ifmcaddr6 *im6; QETH_CARD_TEXT(card, 4, "addmc"); - for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; - im4 = rcu_dereference(im4->next_rcu)) { - qeth_l3_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev); - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!ipm) - continue; - ipm->u.a4.addr = im4->multiaddr; - memcpy(ipm->mac, buf, OSA_ADDR_LEN); - ipm->is_multicast = 1; - if (!qeth_l3_add_ip(card, ipm)) - kfree(ipm); - } -} - -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc(struct qeth_card *card) -{ - struct in_device *in_dev; - u16 vid; - - QETH_CARD_TEXT(card, 4, "addmcvl"); - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; + if (!dev || !(dev->flags & IFF_UP)) + goto out; - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), - vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = __in_dev_get_rcu(netdev); - if (!in_dev) - continue; - qeth_l3_add_mc(card, in_dev); - } -} + in4_dev = __in_dev_get_rtnl(dev); + if (!in4_dev) + goto walk_ipv6; -static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) -{ - struct in_device *in4_dev; + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; - QETH_CARD_TEXT(card, 4, "chkmcv4"); - rcu_read_lock(); - in4_dev = __in_dev_get_rcu(card->dev); - if (in4_dev == NULL) - goto unlock; - qeth_l3_add_mc(card, in4_dev); - qeth_l3_add_vlan_mc(card); -unlock: - rcu_read_unlock(); -} + for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rtnl_dereference(im4->next_rcu)) { + tmp.u.a4.addr = im4->multiaddr; -#ifdef CONFIG_QETH_IPV6 -static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) -{ - struct qeth_ipaddr *ipm; - struct ifmcaddr6 *im6; - char buf[MAX_ADDR_LEN]; + ipm = qeth_l3_find_addr_by_ip(card, &tmp); + if (ipm) { + /* for mcast, by-IP match means full match */ + ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + continue; + } - QETH_CARD_TEXT(card, 4, "addmc6"); - for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { - ndisc_mc_map(&im6->mca_addr, buf, in6_dev->dev, 0); - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); + ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL); if (!ipm) continue; - ipm->is_multicast = 1; - memcpy(ipm->mac, buf, OSA_ADDR_LEN); - memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr, - sizeof(struct in6_addr)); - if (!qeth_l3_add_ip(card, ipm)) - kfree(ipm); - } -} - -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc6(struct qeth_card *card) -{ - struct inet6_dev *in_dev; - u16 vid; - QETH_CARD_TEXT(card, 4, "admc6vl"); - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), - vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = in6_dev_get(netdev); - if (!in_dev) - continue; - read_lock_bh(&in_dev->lock); - qeth_l3_add_mc6(card, in_dev); - read_unlock_bh(&in_dev->lock); - in6_dev_put(in_dev); + hash_add(card->rx_mode_addrs, &ipm->hnode, + qeth_l3_ipaddr_hash(ipm)); } -} - -static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) -{ - struct inet6_dev *in6_dev; - QETH_CARD_TEXT(card, 4, "chkmcv6"); +walk_ipv6: if (!qeth_is_supported(card, IPA_IPV6)) - return ; - in6_dev = in6_dev_get(card->dev); - if (in6_dev == NULL) - return; - rcu_read_lock(); - read_lock_bh(&in6_dev->lock); - qeth_l3_add_mc6(card, in6_dev); - qeth_l3_add_vlan_mc6(card); - read_unlock_bh(&in6_dev->lock); - rcu_read_unlock(); - in6_dev_put(in6_dev); -} -#endif /* CONFIG_QETH_IPV6 */ - -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(card->dev, htons(ETH_P_8021Q), vid); - if (!netdev) - return; - in_dev = in_dev_get(netdev); - if (!in_dev) - return; - for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (addr) { - addr->u.a4.addr = ifa->ifa_address; - addr->u.a4.mask = ifa->ifa_mask; - addr->type = QETH_IP_TYPE_NORMAL; - if (!qeth_l3_delete_ip(card, addr)) - kfree(addr); - } - } - in_dev_put(in_dev); -} - -static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, - unsigned short vid) -{ -#ifdef CONFIG_QETH_IPV6 - struct inet6_dev *in6_dev; - struct inet6_ifaddr *ifa; - struct qeth_ipaddr *addr; - struct net_device *netdev; - - QETH_CARD_TEXT(card, 4, "frvaddr6"); + goto out; - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid); - if (!netdev) - return; - in6_dev = in6_dev_get(netdev); + in6_dev = __in6_dev_get(dev); if (!in6_dev) - return; - list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); - if (addr) { - memcpy(&addr->u.a6.addr, &ifa->addr, - sizeof(struct in6_addr)); - addr->u.a6.pfxlen = ifa->prefix_len; - addr->type = QETH_IP_TYPE_NORMAL; - if (!qeth_l3_delete_ip(card, addr)) - kfree(addr); - } - } - in6_dev_put(in6_dev); -#endif /* CONFIG_QETH_IPV6 */ -} - -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) -{ - struct qeth_card *card = dev->ml_priv; - - set_bit(vid, card->active_vlans); - return 0; -} - -static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, - __be16 proto, u16 vid) -{ - struct qeth_card *card = dev->ml_priv; - unsigned long flags; - - QETH_CARD_TEXT_(card, 4, "kid:%d", vid); - if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { - QETH_CARD_TEXT(card, 3, "kidREC"); - return 0; - } - spin_lock_irqsave(&card->vlanlock, flags); - /* unregister IP addresses of vlan device */ - qeth_l3_free_vlan_addresses(card, vid); - clear_bit(vid, card->active_vlans); - spin_unlock_irqrestore(&card->vlanlock, flags); - qeth_l3_set_multicast_list(card->dev); - return 0; -} - -static inline int qeth_l3_rebuild_skb(struct qeth_card *card, - struct sk_buff *skb, struct qeth_hdr *hdr, - unsigned short *vlan_id) -{ - __be16 prot; - struct iphdr *ip_hdr; - unsigned char tg_addr[MAX_ADDR_LEN]; - int is_vlan = 0; - - if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) { - prot = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 : - ETH_P_IP); - switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) { - case QETH_CAST_MULTICAST: - switch (prot) { -#ifdef CONFIG_QETH_IPV6 - case __constant_htons(ETH_P_IPV6): - ndisc_mc_map((struct in6_addr *) - skb->data + 24, - tg_addr, card->dev, 0); - break; -#endif - case __constant_htons(ETH_P_IP): - ip_hdr = (struct iphdr *)skb->data; - ip_eth_mc_map(ip_hdr->daddr, tg_addr); - break; - default: - memcpy(tg_addr, card->dev->broadcast, - card->dev->addr_len); - } - card->stats.multicast++; - skb->pkt_type = PACKET_MULTICAST; - break; - case QETH_CAST_BROADCAST: - memcpy(tg_addr, card->dev->broadcast, - card->dev->addr_len); - card->stats.multicast++; - skb->pkt_type = PACKET_BROADCAST; - break; - case QETH_CAST_UNICAST: - case QETH_CAST_ANYCAST: - case QETH_CAST_NOCAST: - default: - if (card->options.sniffer) - skb->pkt_type = PACKET_OTHERHOST; - else - skb->pkt_type = PACKET_HOST; - memcpy(tg_addr, card->dev->dev_addr, - card->dev->addr_len); - } - if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) - card->dev->header_ops->create(skb, card->dev, prot, - tg_addr, &hdr->hdr.l3.dest_addr[2], - card->dev->addr_len); - else - card->dev->header_ops->create(skb, card->dev, prot, - tg_addr, "FAKELL", card->dev->addr_len); - } + goto out; - skb->protocol = eth_type_trans(skb, card->dev); + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; - if (hdr->hdr.l3.ext_flags & - (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) { - *vlan_id = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME) ? - hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]); - is_vlan = 1; - } + for (im6 = rtnl_dereference(in6_dev->mc_list); + im6; + im6 = rtnl_dereference(im6->next)) { + tmp.u.a6.addr = im6->mca_addr; - 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; - - return is_vlan; -} - -static int qeth_l3_process_inbound_buffer(struct qeth_card *card, - int budget, int *done) -{ - int work_done = 0; - struct sk_buff *skb; - struct qeth_hdr *hdr; - __u16 vlan_tag = 0; - int is_vlan; - unsigned int len; - __u16 magic; - - *done = 0; - WARN_ON_ONCE(!budget); - while (budget) { - skb = qeth_core_get_next_skb(card, - &card->qdio.in_q->bufs[card->rx.b_index], - &card->rx.b_element, &card->rx.e_offset, &hdr); - if (!skb) { - *done = 1; - break; - } - skb->dev = card->dev; - switch (hdr->hdr.l3.id) { - case QETH_HEADER_TYPE_LAYER3: - magic = *(__u16 *)skb->data; - if ((card->info.type == QETH_CARD_TYPE_IQD) && - (magic == ETH_P_AF_IUCV)) { - skb->protocol = ETH_P_AF_IUCV; - skb->pkt_type = PACKET_HOST; - skb->mac_header = NET_SKB_PAD; - skb->dev = card->dev; - len = skb->len; - card->dev->header_ops->create(skb, card->dev, 0, - card->dev->dev_addr, "FAKELL", - card->dev->addr_len); - netif_receive_skb(skb); - } else { - is_vlan = qeth_l3_rebuild_skb(card, skb, hdr, - &vlan_tag); - len = skb->len; - if (is_vlan && !card->options.sniffer) - __vlan_hwaccel_put_tag(skb, - htons(ETH_P_8021Q), vlan_tag); - napi_gro_receive(&card->napi, skb); - } - break; - case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */ - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - len = skb->len; - netif_receive_skb(skb); - break; - default: - dev_kfree_skb_any(skb); - QETH_CARD_TEXT(card, 3, "inbunkno"); - QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN); + ipm = qeth_l3_find_addr_by_ip(card, &tmp); + if (ipm) { + /* for mcast, by-IP match means full match */ + ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; continue; } - work_done++; - budget--; - card->stats.rx_packets++; - card->stats.rx_bytes += len; - } - return work_done; -} - -static int qeth_l3_poll(struct napi_struct *napi, int budget) -{ - struct qeth_card *card = container_of(napi, struct qeth_card, napi); - int work_done = 0; - struct qeth_qdio_buffer *buffer; - int done; - int new_budget = budget; - - if (card->options.performance_stats) { - card->perf_stats.inbound_cnt++; - card->perf_stats.inbound_start_time = qeth_get_micros(); - } - while (1) { - if (!card->rx.b_count) { - card->rx.qdio_err = 0; - card->rx.b_count = qdio_get_next_buffers( - card->data.ccwdev, 0, &card->rx.b_index, - &card->rx.qdio_err); - if (card->rx.b_count <= 0) { - card->rx.b_count = 0; - break; - } - card->rx.b_element = - &card->qdio.in_q->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } + ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC); + if (!ipm) + continue; - 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_l3_process_inbound_buffer( - card, new_budget, &done); - else - done = 1; - - if (done) { - if (card->options.performance_stats) - card->perf_stats.bufs_rec++; - qeth_put_buffer_pool_entry(card, - buffer->pool_entry); - qeth_queue_input_buffer(card, card->rx.b_index); - card->rx.b_count--; - if (card->rx.b_count) { - card->rx.b_index = - (card->rx.b_index + 1) % - QDIO_MAX_BUFFERS_PER_Q; - card->rx.b_element = - &card->qdio.in_q - ->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } - } + hash_add(card->rx_mode_addrs, &ipm->hnode, + qeth_l3_ipaddr_hash(ipm)); - if (work_done >= budget) - goto out; - else - new_budget = budget - work_done; - } } - napi_complete(napi); - if (qdio_start_irq(card->data.ccwdev, 0)) - napi_schedule(&card->napi); out: - if (card->options.performance_stats) - card->perf_stats.inbound_time += qeth_get_micros() - - card->perf_stats.inbound_start_time; - return work_done; -} - -static int qeth_l3_verify_vlan_dev(struct net_device *dev, - struct qeth_card *card) -{ - int rc = 0; - u16 vid; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - rcu_read_lock(); - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), - vid); - rcu_read_unlock(); - if (netdev == dev) { - rc = QETH_VLAN_CARD; - break; - } - } - - if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card)) - return 0; - - return rc; -} - -static int qeth_l3_verify_dev(struct net_device *dev) -{ - struct qeth_card *card; - unsigned long flags; - int rc = 0; - - read_lock_irqsave(&qeth_core_card_list.rwlock, flags); - list_for_each_entry(card, &qeth_core_card_list.list, list) { - if (card->dev == dev) { - rc = QETH_REAL_CARD; - break; - } - rc = qeth_l3_verify_vlan_dev(dev, card); - if (rc) - break; - } - read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); - - return rc; -} - -static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) -{ - struct qeth_card *card = NULL; - int rc; - - rc = qeth_l3_verify_dev(dev); - if (rc == QETH_REAL_CARD) - card = dev->ml_priv; - else if (rc == QETH_VLAN_CARD) - card = vlan_dev_real_dev(dev)->ml_priv; - if (card && card->options.layer2) - card = NULL; - if (card) - QETH_CARD_TEXT_(card, 4, "%d", rc); - return card ; -} - -static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) -{ - int rc = 0; - - QETH_DBF_TEXT(SETUP, 2, "stopcard"); - QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); - - qeth_set_allowed_threads(card, 0, 1); - if (card->options.sniffer && - (card->info.promisc_mode == SET_PROMISC_MODE_ON)) - qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE); - if (card->read.state == CH_STATE_UP && - card->write.state == CH_STATE_UP && - (card->state == CARD_STATE_UP)) { - if (recovery_mode) - qeth_l3_stop(card->dev); - else { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); - } - card->state = CARD_STATE_SOFTSETUP; - } - if (card->state == CARD_STATE_SOFTSETUP) { - qeth_l3_clear_ip_list(card, 1); - qeth_clear_ipacmd_list(card); - card->state = CARD_STATE_HARDSETUP; - } - if (card->state == CARD_STATE_HARDSETUP) { - qeth_qdio_clear_card(card, 0); - qeth_clear_qdio_buffers(card); - qeth_clear_working_pool_list(card); - card->state = CARD_STATE_DOWN; - } - if (card->state == CARD_STATE_DOWN) { - qeth_clear_cmd_buffers(&card->read); - qeth_clear_cmd_buffers(&card->write); - } - return rc; + return 0; } -/* - * test for and Switch promiscuous mode (on or off) - * either for guestlan or HiperSocket Sniffer - */ -static void -qeth_l3_handle_promisc_mode(struct qeth_card *card) +static void qeth_l3_set_promisc_mode(struct qeth_card *card) { - struct net_device *dev = card->dev; + bool enable = card->dev->flags & IFF_PROMISC; - if (((dev->flags & IFF_PROMISC) && - (card->info.promisc_mode == SET_PROMISC_MODE_ON)) || - (!(dev->flags & IFF_PROMISC) && - (card->info.promisc_mode == SET_PROMISC_MODE_OFF))) + if (card->info.promisc_mode == enable) return; - if (card->info.guestlan) { /* Guestlan trace */ + if (IS_VM_NIC(card)) { /* Guestlan trace */ if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) - qeth_setadp_promisc_mode(card); + qeth_setadp_promisc_mode(card, enable); } else if (card->options.sniffer && /* HiperSockets trace */ qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { - if (dev->flags & IFF_PROMISC) { + if (enable) { QETH_CARD_TEXT(card, 3, "+promisc"); qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE); } else { @@ -2213,53 +1149,79 @@ qeth_l3_handle_promisc_mode(struct qeth_card *card) } } -static void qeth_l3_set_multicast_list(struct net_device *dev) +static void qeth_l3_rx_mode_work(struct work_struct *work) { - struct qeth_card *card = dev->ml_priv; + struct qeth_card *card = container_of(work, struct qeth_card, + rx_mode_work); + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i, rc; QETH_CARD_TEXT(card, 3, "setmulti"); - if (qeth_threads_running(card, QETH_RECOVER_THREAD) && - (card->state != CARD_STATE_UP)) - return; + if (!card->options.sniffer) { - qeth_l3_delete_mc_addresses(card); - qeth_l3_add_multicast_ipv4(card); -#ifdef CONFIG_QETH_IPV6 - qeth_l3_add_multicast_ipv6(card); -#endif - qeth_l3_set_ip_addr_list(card); - if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) - return; + rtnl_lock(); + qeth_l3_add_mcast_rtnl(card->dev, 0, card); + if (qeth_is_supported(card, IPA_FULL_VLAN)) + vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card); + rtnl_unlock(); + + hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) { + switch (addr->disp_flag) { + case QETH_DISP_ADDR_DELETE: + rc = qeth_l3_deregister_addr_entry(card, addr); + if (!rc || rc == -ENOENT) { + hash_del(&addr->hnode); + kfree(addr); + } + break; + case QETH_DISP_ADDR_ADD: + rc = qeth_l3_register_addr_entry(card, addr); + if (rc && rc != -ENETDOWN) { + hash_del(&addr->hnode); + kfree(addr); + break; + } + fallthrough; + default: + /* for next call to set_rx_mode(): */ + addr->disp_flag = QETH_DISP_ADDR_DELETE; + } + } } - qeth_l3_handle_promisc_mode(card); + + qeth_l3_set_promisc_mode(card); } -static const char *qeth_l3_arp_get_error_cause(int *rc) +static int qeth_l3_arp_makerc(u16 rc) { - switch (*rc) { - case QETH_IPA_ARP_RC_FAILED: - *rc = -EIO; - return "operation failed"; + switch (rc) { + case IPA_RC_SUCCESS: + return 0; case QETH_IPA_ARP_RC_NOTSUPP: - *rc = -EOPNOTSUPP; - return "operation not supported"; - case QETH_IPA_ARP_RC_OUT_OF_RANGE: - *rc = -EINVAL; - return "argument out of range"; case QETH_IPA_ARP_RC_Q_NOTSUPP: - *rc = -EOPNOTSUPP; - return "query operation not supported"; + return -EOPNOTSUPP; + case QETH_IPA_ARP_RC_OUT_OF_RANGE: + return -EINVAL; case QETH_IPA_ARP_RC_Q_NO_DATA: - *rc = -ENOENT; - return "no query data available"; + return -ENOENT; default: - return "unknown error"; + return -EIO; } } +static int qeth_l3_arp_cmd_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; + + qeth_setassparms_cb(card, reply, data); + return qeth_l3_arp_makerc(cmd->hdr.return_code); +} + static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) { - int tmp; + struct qeth_cmd_buffer *iob; int rc; QETH_CARD_TEXT(card, 3, "arpstnoe"); @@ -2269,20 +1231,24 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES; * thus we say EOPNOTSUPP for this ARP function */ - if (card->info.guestlan) + if (IS_VM_NIC(card)) return -EOPNOTSUPP; if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_SET_NO_ENTRIES, - no_entries); - if (rc) { - tmp = rc; - QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on " - "%s: %s (0x%x/%d)\n", QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); - } + + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_SET_NO_ENTRIES, + SETASS_DATA_SIZEOF(flags_32bit), + QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; + + __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (u32) no_entries; + rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); + if (rc) + QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on device %x: %#x\n", + CARD_DEVID(card), rc); return rc; } @@ -2334,7 +1300,6 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, struct qeth_ipa_cmd *cmd; struct qeth_arp_query_data *qdata; struct qeth_arp_query_info *qinfo; - int i; int e; int entrybytes_done; int stripped_bytes; @@ -2348,13 +1313,13 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, if (cmd->hdr.return_code) { QETH_CARD_TEXT(card, 4, "arpcberr"); QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); - return 0; + return qeth_l3_arp_makerc(cmd->hdr.return_code); } if (cmd->data.setassparms.hdr.return_code) { cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; QETH_CARD_TEXT(card, 4, "setaperr"); QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); - return 0; + return qeth_l3_arp_makerc(cmd->hdr.return_code); } qdata = &cmd->data.setassparms.data.query_arp; QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); @@ -2381,9 +1346,9 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, break; if ((qinfo->udata_len - qinfo->udata_offset) < esize) { - QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); - cmd->hdr.return_code = IPA_RC_ENOMEM; - goto out_error; + QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOSPC); + memset(qinfo->udata, 0, 4); + return -ENOSPC; } memcpy(qinfo->udata + qinfo->udata_offset, @@ -2406,25 +1371,6 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); QETH_CARD_TEXT_(card, 4, "rc%i", 0); return 0; -out_error: - i = 0; - memcpy(qinfo->udata, &i, 4); - return 0; -} - -static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card, - struct qeth_cmd_buffer *iob, int len, - int (*reply_cb)(struct qeth_card *, struct qeth_reply *, - unsigned long), - void *reply_param) -{ - QETH_CARD_TEXT(card, 4, "sendarp"); - - memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); - memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), - &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); - return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob, - reply_cb, reply_param); } static int qeth_l3_query_arp_cache_info(struct qeth_card *card, @@ -2433,30 +1379,21 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card, { struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - int tmp; int rc; QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_QUERY_INFO, - sizeof(struct qeth_arp_query_data) - sizeof(char), - prot); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_QUERY_INFO, + SETASS_DATA_SIZEOF(query_arp), prot); + if (!iob) + return -ENOMEM; + cmd = __ipa_cmd(iob); cmd->data.setassparms.data.query_arp.request_bits = 0x000F; - cmd->data.setassparms.data.query_arp.reply_bits = 0; - cmd->data.setassparms.data.query_arp.no_entries = 0; - rc = qeth_l3_send_ipa_arp_cmd(card, iob, - QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, - qeth_l3_arp_query_cb, (void *)qinfo); - if (rc) { - tmp = rc; - QETH_DBF_MESSAGE(2, - "Error while querying ARP cache on %s: %s " - "(0x%x/%d)\n", QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); - } - + rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_query_cb, qinfo); + if (rc) + QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n", + CARD_DEVID(card), rc); return rc; } @@ -2488,110 +1425,69 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) if (rc) { if (copy_to_user(udata, qinfo.udata, 4)) rc = -EFAULT; - goto free_and_out; - } else { -#ifdef CONFIG_QETH_IPV6 - if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { - /* fails in case of GuestLAN QDIO mode */ - qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, - &qinfo); - } -#endif - if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { - QETH_CARD_TEXT(card, 4, "qactf"); - rc = -EFAULT; - goto free_and_out; - } - QETH_CARD_TEXT_(card, 4, "qacts"); + goto free_and_out; + } + if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { + /* fails in case of GuestLAN QDIO mode */ + qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, &qinfo); } + if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { + QETH_CARD_TEXT(card, 4, "qactf"); + rc = -EFAULT; + goto free_and_out; + } + QETH_CARD_TEXT(card, 4, "qacts"); + free_and_out: kfree(qinfo.udata); out: return rc; } -static int qeth_l3_arp_add_entry(struct qeth_card *card, - struct qeth_arp_cache_entry *entry) +static int qeth_l3_arp_modify_entry(struct qeth_card *card, + struct qeth_arp_cache_entry *entry, + enum qeth_arp_process_subcmds arp_cmd) { + struct qeth_arp_cache_entry *cmd_entry; struct qeth_cmd_buffer *iob; - char buf[16]; - int tmp; int rc; - QETH_CARD_TEXT(card, 3, "arpadent"); + if (arp_cmd == IPA_CMD_ASS_ARP_ADD_ENTRY) + QETH_CARD_TEXT(card, 3, "arpadd"); + else + QETH_CARD_TEXT(card, 3, "arpdel"); /* * currently GuestLAN only supports the ARP assist function * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY; * thus we say EOPNOTSUPP for this ARP function */ - if (card->info.guestlan) + if (IS_VM_NIC(card)) return -EOPNOTSUPP; if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_ADD_ENTRY, - sizeof(struct qeth_arp_cache_entry), + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, arp_cmd, + SETASS_DATA_SIZEOF(arp_entry), QETH_PROT_IPV4); - rc = qeth_l3_send_setassparms(card, iob, - sizeof(struct qeth_arp_cache_entry), - (unsigned long) entry, - qeth_l3_default_setassparms_cb, NULL); - if (rc) { - tmp = rc; - qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf); - QETH_DBF_MESSAGE(2, "Could not add ARP entry for address %s " - "on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); - } - return rc; -} - -static int qeth_l3_arp_remove_entry(struct qeth_card *card, - struct qeth_arp_cache_entry *entry) -{ - struct qeth_cmd_buffer *iob; - char buf[16] = {0, }; - int tmp; - int rc; - - QETH_CARD_TEXT(card, 3, "arprment"); + if (!iob) + return -ENOMEM; - /* - * currently GuestLAN only supports the ARP assist function - * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY; - * thus we say EOPNOTSUPP for this ARP function - */ - if (card->info.guestlan) - return -EOPNOTSUPP; - if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { - return -EOPNOTSUPP; - } - memcpy(buf, entry, 12); - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_REMOVE_ENTRY, - 12, - QETH_PROT_IPV4); - rc = qeth_l3_send_setassparms(card, iob, - 12, (unsigned long)buf, - qeth_l3_default_setassparms_cb, NULL); - if (rc) { - tmp = rc; - memset(buf, 0, 16); - qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf); - QETH_DBF_MESSAGE(2, "Could not delete ARP entry for address %s" - " on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); - } + cmd_entry = &__ipa_cmd(iob)->data.setassparms.data.arp_entry; + ether_addr_copy(cmd_entry->macaddr, entry->macaddr); + memcpy(cmd_entry->ipaddr, entry->ipaddr, 4); + rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); + if (rc) + QETH_DBF_MESSAGE(2, "Could not modify (cmd: %#x) ARP entry on device %x: %#x\n", + arp_cmd, CARD_DEVID(card), rc); return rc; } static int qeth_l3_arp_flush_cache(struct qeth_card *card) { + struct qeth_cmd_buffer *iob; int rc; - int tmp; QETH_CARD_TEXT(card, 3, "arpflush"); @@ -2600,36 +1496,33 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card) * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE; * thus we say EOPNOTSUPP for this ARP function */ - if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD)) + if (IS_VM_NIC(card) || IS_IQD(card)) return -EOPNOTSUPP; if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_FLUSH_CACHE, 0); - if (rc) { - tmp = rc; - QETH_DBF_MESSAGE(2, "Could not flush ARP cache on %s: %s " - "(0x%x/%d)\n", QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); - } + + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_FLUSH_CACHE, 0, + QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; + + rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); + if (rc) + QETH_DBF_MESSAGE(2, "Could not flush ARP cache on device %x: %#x\n", + CARD_DEVID(card), rc); return rc; } -static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static int qeth_l3_ndo_siocdevprivate(struct net_device *dev, struct ifreq *rq, + void __user *data, int cmd) { struct qeth_card *card = dev->ml_priv; struct qeth_arp_cache_entry arp_entry; - struct mii_ioctl_data *mii_data; + enum qeth_arp_process_subcmds arp_cmd; int rc = 0; - if (!card) - return -ENODEV; - - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_SOFTSETUP)) - return -ENODEV; - switch (cmd) { case SIOC_QETH_ARP_SET_NO_ENTRIES: if (!capable(CAP_NET_ADMIN)) { @@ -2643,30 +1536,19 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) rc = -EPERM; break; } - rc = qeth_l3_arp_query(card, rq->ifr_ifru.ifru_data); + rc = qeth_l3_arp_query(card, data); break; case SIOC_QETH_ARP_ADD_ENTRY: - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data, - sizeof(struct qeth_arp_cache_entry))) - rc = -EFAULT; - else - rc = qeth_l3_arp_add_entry(card, &arp_entry); - break; case SIOC_QETH_ARP_REMOVE_ENTRY: - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data, - sizeof(struct qeth_arp_cache_entry))) - rc = -EFAULT; - else - rc = qeth_l3_arp_remove_entry(card, &arp_entry); - break; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&arp_entry, data, sizeof(arp_entry))) + return -EFAULT; + + arp_cmd = (cmd == SIOC_QETH_ARP_ADD_ENTRY) ? + IPA_CMD_ASS_ARP_ADD_ENTRY : + IPA_CMD_ASS_ARP_REMOVE_ENTRY; + return qeth_l3_arp_modify_entry(card, &arp_entry, arp_cmd); case SIOC_QETH_ARP_FLUSH_CACHE: if (!capable(CAP_NET_ADMIN)) { rc = -EPERM; @@ -2674,532 +1556,234 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } rc = qeth_l3_arp_flush_cache(card); break; - case SIOC_QETH_ADP_SET_SNMP_CONTROL: - rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); - break; - case SIOC_QETH_GET_CARD_TYPE: - if ((card->info.type == QETH_CARD_TYPE_OSD || - card->info.type == QETH_CARD_TYPE_OSX) && - !card->info.guestlan) - return 1; - return 0; - break; - case SIOCGMIIPHY: - mii_data = if_mii(rq); - mii_data->phy_id = 0; - break; - case SIOCGMIIREG: - mii_data = if_mii(rq); - if (mii_data->phy_id != 0) - rc = -EINVAL; - else - mii_data->val_out = qeth_mdio_read(dev, - mii_data->phy_id, - mii_data->reg_num); - break; - case SIOC_QETH_QUERY_OAT: - rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); - break; default: - rc = -EOPNOTSUPP; + rc = qeth_siocdevprivate(dev, rq, data, cmd); } - if (rc) - QETH_CARD_TEXT_(card, 2, "ioce%d", rc); return rc; } -int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) +static int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst, + __be16 proto) { - int cast_type = RTN_UNSPEC; struct neighbour *n = NULL; - struct dst_entry *dst; - rcu_read_lock(); - dst = skb_dst(skb); if (dst) n = dst_neigh_lookup_skb(dst, skb); + if (n) { - cast_type = n->type; - rcu_read_unlock(); + int cast_type = n->type; + neigh_release(n); if ((cast_type == RTN_BROADCAST) || (cast_type == RTN_MULTICAST) || (cast_type == RTN_ANYCAST)) return cast_type; - else - return RTN_UNSPEC; + return RTN_UNICAST; + } + + /* no neighbour (eg AF_PACKET), fall back to target's IP address ... */ + switch (proto) { + case htons(ETH_P_IP): + if (ipv4_is_lbcast(ip_hdr(skb)->daddr)) + return RTN_BROADCAST; + return ipv4_is_multicast(ip_hdr(skb)->daddr) ? + RTN_MULTICAST : RTN_UNICAST; + case htons(ETH_P_IPV6): + return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ? + RTN_MULTICAST : RTN_UNICAST; + case htons(ETH_P_AF_IUCV): + return RTN_UNICAST; + default: + /* OSA only: ... and MAC address */ + return qeth_get_ether_cast_type(skb); } +} + +static int qeth_l3_get_cast_type(struct sk_buff *skb, __be16 proto) +{ + struct dst_entry *dst; + int cast_type; + + rcu_read_lock(); + dst = qeth_dst_check_rcu(skb, proto); + cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto); rcu_read_unlock(); - /* try something else */ - if (skb->protocol == ETH_P_IPV6) - return (skb_network_header(skb)[24] == 0xff) ? - RTN_MULTICAST : 0; - else if (skb->protocol == ETH_P_IP) - return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ? - RTN_MULTICAST : 0; - /* ... */ - if (!memcmp(skb->data, skb->dev->broadcast, 6)) - return RTN_BROADCAST; - else { - u16 hdr_mac; - - hdr_mac = *((u16 *)skb->data); - /* tr multicast? */ - switch (card->info.link_type) { - case QETH_LINK_TYPE_HSTR: - case QETH_LINK_TYPE_LANE_TR: - if ((hdr_mac == QETH_TR_MAC_NC) || - (hdr_mac == QETH_TR_MAC_C)) - return RTN_MULTICAST; - break; - /* eth or so multicast? */ - default: - if ((hdr_mac == QETH_ETH_MAC_V4) || - (hdr_mac == QETH_ETH_MAC_V6)) - return RTN_MULTICAST; - } - } return cast_type; } -static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card, - struct qeth_hdr *hdr, struct sk_buff *skb) +static u8 qeth_l3_cast_type_to_flag(int cast_type) { - char daddr[16]; - struct af_iucv_trans_hdr *iucv_hdr; - - skb_pull(skb, 14); - card->dev->header_ops->create(skb, card->dev, 0, - card->dev->dev_addr, card->dev->dev_addr, - card->dev->addr_len); - skb_pull(skb, 14); - iucv_hdr = (struct af_iucv_trans_hdr *)skb->data; - memset(hdr, 0, sizeof(struct qeth_hdr)); - hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; - hdr->hdr.l3.ext_flags = 0; - hdr->hdr.l3.length = skb->len; - hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; - memset(daddr, 0, sizeof(daddr)); - daddr[0] = 0xfe; - daddr[1] = 0x80; - memcpy(&daddr[8], iucv_hdr->destUserID, 8); - memcpy(hdr->hdr.l3.dest_addr, daddr, 16); + if (cast_type == RTN_MULTICAST) + return QETH_CAST_MULTICAST; + if (cast_type == RTN_ANYCAST) + return QETH_CAST_ANYCAST; + if (cast_type == RTN_BROADCAST) + return QETH_CAST_BROADCAST; + return QETH_CAST_UNICAST; } -static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, - struct sk_buff *skb, int ipv, int cast_type) +static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue, + struct qeth_hdr *hdr, struct sk_buff *skb, + __be16 proto, unsigned int data_len) { + struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3; + struct vlan_ethhdr *veth = vlan_eth_hdr(skb); + struct qeth_card *card = queue->card; struct dst_entry *dst; + int cast_type; - memset(hdr, 0, sizeof(struct qeth_hdr)); - hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; - hdr->hdr.l3.ext_flags = 0; + hdr->hdr.l3.length = data_len; - /* - * before we're going to overwrite this location with next hop ip. - * v6 uses passthrough, v4 sets the tag in the QDIO header. - */ - if (vlan_tx_tag_present(skb)) { - if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD)) - hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME; - else - hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG; - hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb); + if (skb_is_gso(skb)) { + hdr->hdr.l3.id = QETH_HEADER_TYPE_L3_TSO; + } else { + hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, proto); + /* some HW requires combined L3+L4 csum offload: */ + if (proto == htons(ETH_P_IP)) + hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ; + } } - hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr); + if (proto == htons(ETH_P_IP) || IS_IQD(card)) { + /* NETIF_F_HW_VLAN_CTAG_TX */ + if (skb_vlan_tag_present(skb)) { + hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_VLAN_FRAME; + hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb); + } + } else if (veth->h_vlan_proto == htons(ETH_P_8021Q)) { + hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_INCLUDE_VLAN_TAG; + hdr->hdr.l3.vlan_id = ntohs(veth->h_vlan_TCI); + } rcu_read_lock(); - dst = skb_dst(skb); - if (ipv == 4) { - struct rtable *rt = (struct rtable *) dst; - __be32 *pkey = &ip_hdr(skb)->daddr; - - if (rt->rt_gateway) - pkey = &rt->rt_gateway; - - /* IPv4 */ - hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type); - memset(hdr->hdr.l3.dest_addr, 0, 12); - *((__be32 *) (&hdr->hdr.l3.dest_addr[12])) = *pkey; - } else if (ipv == 6) { - struct rt6_info *rt = (struct rt6_info *) dst; - struct in6_addr *pkey = &ipv6_hdr(skb)->daddr; - - if (!ipv6_addr_any(&rt->rt6i_gateway)) - pkey = &rt->rt6i_gateway; - - /* IPv6 */ - hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type); - if (card->info.type == QETH_CARD_TYPE_IQD) - hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU; - memcpy(hdr->hdr.l3.dest_addr, pkey, 16); - } else { - if (!memcmp(skb->data + sizeof(struct qeth_hdr), - skb->dev->broadcast, 6)) { - /* broadcast? */ - hdr->hdr.l3.flags = QETH_CAST_BROADCAST | - QETH_HDR_PASSTHRU; - } else { - hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ? - QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU : - QETH_CAST_UNICAST | QETH_HDR_PASSTHRU; - } + dst = qeth_dst_check_rcu(skb, proto); + + if (IS_IQD(card) && skb_get_queue_mapping(skb) != QETH_IQD_MCAST_TXQ) + cast_type = RTN_UNICAST; + else + cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto); + l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type); + + switch (proto) { + case htons(ETH_P_IP): + l3_hdr->next_hop.addr.s6_addr32[3] = + qeth_next_hop_v4_rcu(skb, dst); + break; + case htons(ETH_P_IPV6): + l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst); + + hdr->hdr.l3.flags |= QETH_HDR_IPV6; + if (!IS_IQD(card)) + hdr->hdr.l3.flags |= QETH_HDR_PASSTHRU; + break; + case htons(ETH_P_AF_IUCV): + l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80); + memcpy(&l3_hdr->next_hop.addr.s6_addr32[2], + iucv_trans_hdr(skb)->destUserID, 8); + l3_hdr->flags |= QETH_HDR_IPV6; + break; + default: + /* OSA only: */ + l3_hdr->flags |= QETH_HDR_PASSTHRU; } rcu_read_unlock(); } -static inline void qeth_l3_hdr_csum(struct qeth_card *card, - struct qeth_hdr *hdr, struct sk_buff *skb) +static void qeth_l3_fixup_headers(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) -{ - struct qeth_hdr_tso *hdr = (struct qeth_hdr_tso *)qhdr; - struct tcphdr *tcph = tcp_hdr(skb); - struct iphdr *iph = ip_hdr(skb); - struct ipv6hdr *ip6h = ipv6_hdr(skb); - - /*fix header to TSO values ...*/ - hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; - hdr->hdr.hdr.l3.length = skb->len - sizeof(struct qeth_hdr_tso); - /*set values which are fix for the first approach ...*/ - hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); - hdr->ext.imb_hdr_no = 1; - hdr->ext.hdr_type = 1; - hdr->ext.hdr_version = 1; - hdr->ext.hdr_len = 28; - /*insert non-fix values */ - hdr->ext.mss = skb_shinfo(skb)->gso_size; - hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); - hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - - sizeof(struct qeth_hdr_tso)); - tcph->check = 0; - if (skb->protocol == ETH_P_IPV6) { - ip6h->payload_len = 0; - tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - 0, IPPROTO_TCP, 0); - } else { - /*OSA want us to set these values ...*/ - tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - 0, IPPROTO_TCP, 0); - iph->tot_len = 0; + /* this is safe, IPv6 traffic takes a different path */ + if (skb->ip_summed == CHECKSUM_PARTIAL) iph->check = 0; + if (skb_is_gso(skb)) { + iph->tot_len = 0; + tcp_hdr(skb)->check = ~tcp_v4_check(0, iph->saddr, + iph->daddr, 0); } } -static inline int qeth_l3_tso_elements(struct sk_buff *skb) +static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, __be16 proto) { - unsigned long tcpd = (unsigned long)tcp_hdr(skb) + - tcp_hdr(skb)->doff * 4; - int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data); - int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); + unsigned int hw_hdr_len; + int rc; - elements += qeth_get_elements_for_frags(skb); + /* re-use the L2 header area for the HW header: */ + hw_hdr_len = skb_is_gso(skb) ? sizeof(struct qeth_hdr_tso) : + sizeof(struct qeth_hdr); + rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN); + if (rc) + return rc; + skb_pull(skb, ETH_HLEN); - return elements; + qeth_l3_fixup_headers(skb); + return qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header); } -static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { - int rc; - u16 *tag; - struct qeth_hdr *hdr = NULL; - int elements_needed = 0; - int elems; struct qeth_card *card = dev->ml_priv; - struct sk_buff *new_skb = NULL; - int ipv = qeth_get_ip_version(skb); - int cast_type = qeth_l3_get_cast_type(card, skb); - struct qeth_qdio_out_q *queue = card->qdio.out_qs - [qeth_get_priority_queue(card, skb, ipv, cast_type)]; - int tx_bytes = skb->len; - bool large_send; - int data_offset = -1; - int nr_frags; - - if (((card->info.type == QETH_CARD_TYPE_IQD) && - (((card->options.cq != QETH_CQ_ENABLED) && !ipv) || - ((card->options.cq == QETH_CQ_ENABLED) && - (skb->protocol != ETH_P_AF_IUCV)))) || - card->options.sniffer) - goto tx_drop; - - if ((card->state != CARD_STATE_UP) || !card->lan_online) { - card->stats.tx_carrier_errors++; - goto tx_drop; - } - - if ((cast_type == RTN_BROADCAST) && - (card->info.broadcast_capable == 0)) - goto tx_drop; - - if (card->options.performance_stats) { - card->perf_stats.outbound_cnt++; - card->perf_stats.outbound_start_time = qeth_get_micros(); - } + __be16 proto = vlan_get_protocol(skb); + u16 txq = skb_get_queue_mapping(skb); + struct qeth_qdio_out_q *queue; + int rc; - large_send = skb_is_gso(skb); + if (!skb_is_gso(skb)) + qdisc_skb_cb(skb)->pkt_len = skb->len; + if (IS_IQD(card)) { + queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)]; - if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && - (skb_shinfo(skb)->nr_frags == 0)) { - new_skb = skb; - if (new_skb->protocol == ETH_P_AF_IUCV) - data_offset = 0; - else - data_offset = ETH_HLEN; - hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); - if (!hdr) + if (card->options.sniffer) goto tx_drop; - elements_needed++; - } else { - /* create a clone with writeable headroom */ - new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) - + VLAN_HLEN); - if (!new_skb) - goto tx_drop; - } - - if (card->info.type == QETH_CARD_TYPE_IQD) { - if (data_offset < 0) - skb_pull(new_skb, ETH_HLEN); - } else { - if (ipv == 4) { - skb_pull(new_skb, ETH_HLEN); - } - - if (ipv != 4 && vlan_tx_tag_present(new_skb)) { - skb_push(new_skb, VLAN_HLEN); - skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); - skb_copy_to_linear_data_offset(new_skb, 4, - new_skb->data + 8, 4); - skb_copy_to_linear_data_offset(new_skb, 8, - new_skb->data + 12, 4); - tag = (u16 *)(new_skb->data + 12); - *tag = __constant_htons(ETH_P_8021Q); - *(tag + 1) = htons(vlan_tx_tag_get(new_skb)); - } - } - - netif_stop_queue(dev); - /* fix hardware limitation: as long as we do not have sbal - * chaining we can not send long frag lists - */ - if (large_send) { - if (qeth_l3_tso_elements(new_skb) + 1 > 16) { - if (skb_linearize(new_skb)) + switch (proto) { + case htons(ETH_P_AF_IUCV): + if (card->options.cq != QETH_CQ_ENABLED) + goto tx_drop; + break; + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + if (card->options.cq == QETH_CQ_ENABLED) goto tx_drop; - if (card->options.performance_stats) - card->perf_stats.tx_lin++; + break; + default: + goto tx_drop; } - } - - if (large_send && (cast_type == RTN_UNSPEC)) { - hdr = (struct qeth_hdr *)skb_push(new_skb, - sizeof(struct qeth_hdr_tso)); - memset(hdr, 0, sizeof(struct qeth_hdr_tso)); - qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); - qeth_tso_fill_header(card, hdr, new_skb); - elements_needed++; } else { - if (data_offset < 0) { - hdr = (struct qeth_hdr *)skb_push(new_skb, - sizeof(struct qeth_hdr)); - qeth_l3_fill_header(card, hdr, new_skb, ipv, - cast_type); - } else { - if (new_skb->protocol == ETH_P_AF_IUCV) - qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb); - else { - qeth_l3_fill_header(card, hdr, new_skb, ipv, - cast_type); - hdr->hdr.l3.length = new_skb->len - data_offset; - } - } - - if (skb->ip_summed == CHECKSUM_PARTIAL) - qeth_l3_hdr_csum(card, hdr, new_skb); + queue = card->qdio.out_qs[txq]; } - elems = qeth_get_elements_no(card, new_skb, elements_needed); - if (!elems) { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); + if (!(dev->flags & IFF_BROADCAST) && + qeth_l3_get_cast_type(skb, proto) == RTN_BROADCAST) goto tx_drop; - } - elements_needed += elems; - nr_frags = skb_shinfo(new_skb)->nr_frags; - - if (card->info.type != QETH_CARD_TYPE_IQD) { - int len; - if (large_send) - len = ((unsigned long)tcp_hdr(new_skb) + - tcp_hdr(new_skb)->doff * 4) - - (unsigned long)new_skb->data; - else - len = sizeof(struct qeth_hdr_layer3); - - if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len)) - goto tx_drop; - rc = qeth_do_send_packet(card, queue, new_skb, hdr, - elements_needed); - } else - rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - elements_needed, data_offset, 0); - if (!rc) { - card->stats.tx_packets++; - card->stats.tx_bytes += tx_bytes; - if (new_skb != skb) - dev_kfree_skb_any(skb); - if (card->options.performance_stats) { - if (large_send) { - card->perf_stats.large_send_bytes += tx_bytes; - card->perf_stats.large_send_cnt++; - } - if (nr_frags) { - card->perf_stats.sg_skbs_sent++; - /* nr_frags + skb->data */ - card->perf_stats.sg_frags_sent += nr_frags + 1; - } - } - rc = NETDEV_TX_OK; - } else { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); - - if (rc == -EBUSY) { - if (new_skb != skb) - dev_kfree_skb_any(new_skb); - return NETDEV_TX_BUSY; - } else - goto tx_drop; - } + if (proto == htons(ETH_P_IP) || IS_IQD(card)) + rc = qeth_l3_xmit(card, skb, queue, proto); + else + rc = qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header); - netif_wake_queue(dev); - if (card->options.performance_stats) - card->perf_stats.outbound_time += qeth_get_micros() - - card->perf_stats.outbound_start_time; - return rc; + if (!rc) + return NETDEV_TX_OK; tx_drop: - card->stats.tx_dropped++; - card->stats.tx_errors++; - if ((new_skb != skb) && new_skb) - dev_kfree_skb_any(new_skb); - dev_kfree_skb_any(skb); - netif_wake_queue(dev); + QETH_TXQ_STAT_INC(queue, tx_dropped); + kfree_skb(skb); return NETDEV_TX_OK; } -static int __qeth_l3_open(struct net_device *dev) -{ - struct qeth_card *card = dev->ml_priv; - int rc = 0; - - QETH_CARD_TEXT(card, 4, "qethopen"); - if (card->state == CARD_STATE_UP) - return rc; - if (card->state != CARD_STATE_SOFTSETUP) - return -ENODEV; - card->data.state = CH_STATE_UP; - card->state = CARD_STATE_UP; - netif_start_queue(dev); - - if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) { - napi_enable(&card->napi); - napi_schedule(&card->napi); - } else - rc = -EIO; - return rc; -} - -static int qeth_l3_open(struct net_device *dev) -{ - struct qeth_card *card = dev->ml_priv; - - QETH_CARD_TEXT(card, 5, "qethope_"); - if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { - QETH_CARD_TEXT(card, 3, "openREC"); - return -ERESTARTSYS; - } - return __qeth_l3_open(dev); -} - -static int qeth_l3_stop(struct net_device *dev) -{ - struct qeth_card *card = dev->ml_priv; - - QETH_CARD_TEXT(card, 4, "qethstop"); - netif_tx_disable(dev); - if (card->state == CARD_STATE_UP) { - card->state = CARD_STATE_SOFTSETUP; - napi_disable(&card->napi); - } - return 0; -} - -static netdev_features_t qeth_l3_fix_features(struct net_device *dev, - netdev_features_t features) -{ - struct qeth_card *card = dev->ml_priv; - - if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) - features &= ~NETIF_F_IP_CSUM; - if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) - features &= ~NETIF_F_TSO; - if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) - features &= ~NETIF_F_RXCSUM; - - return features; -} - -static int qeth_l3_set_features(struct net_device *dev, - netdev_features_t features) +static void qeth_l3_set_rx_mode(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; - u32 changed = dev->features ^ features; - int err; - - if (!(changed & NETIF_F_RXCSUM)) - return 0; - - if (card->state == CARD_STATE_DOWN || - card->state == CARD_STATE_RECOVER) - return 0; - err = qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM); - if (err) - dev->features = features ^ NETIF_F_RXCSUM; - - return err; + schedule_work(&card->rx_mode_work); } -static const struct ethtool_ops qeth_l3_ethtool_ops = { - .get_link = ethtool_op_get_link, - .get_strings = qeth_core_get_strings, - .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_sset_count = qeth_core_get_sset_count, - .get_drvinfo = qeth_core_get_drvinfo, - .get_settings = qeth_core_ethtool_get_settings, -}; - /* * we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting * NOARP on the netdevice is no option because it also turns off neighbor @@ -3224,99 +1808,144 @@ qeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np) return 0; } +static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (vlan_get_protocol(skb) != htons(ETH_P_IP)) + features &= ~NETIF_F_HW_VLAN_CTAG_TX; + return qeth_features_check(skb, dev, features); +} + +static u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + __be16 proto = vlan_get_protocol(skb); + + return qeth_iqd_select_queue(dev, skb, + qeth_l3_get_cast_type(skb, proto), sb_dev); +} + static const struct net_device_ops qeth_l3_netdev_ops = { - .ndo_open = qeth_l3_open, - .ndo_stop = qeth_l3_stop, - .ndo_get_stats = qeth_get_stats, + .ndo_open = qeth_open, + .ndo_stop = qeth_stop, + .ndo_get_stats64 = qeth_get_stats64, .ndo_start_xmit = qeth_l3_hard_start_xmit, + .ndo_select_queue = qeth_l3_iqd_select_queue, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, - .ndo_fix_features = qeth_l3_fix_features, - .ndo_set_features = qeth_l3_set_features, - .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid, + .ndo_set_rx_mode = qeth_l3_set_rx_mode, + .ndo_eth_ioctl = qeth_do_ioctl, + .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate, + .ndo_fix_features = qeth_fix_features, + .ndo_set_features = qeth_set_features, .ndo_tx_timeout = qeth_tx_timeout, }; static const struct net_device_ops qeth_l3_osa_netdev_ops = { - .ndo_open = qeth_l3_open, - .ndo_stop = qeth_l3_stop, - .ndo_get_stats = qeth_get_stats, + .ndo_open = qeth_open, + .ndo_stop = qeth_stop, + .ndo_get_stats64 = qeth_get_stats64, .ndo_start_xmit = qeth_l3_hard_start_xmit, + .ndo_features_check = qeth_l3_osa_features_check, + .ndo_select_queue = qeth_osa_select_queue, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, - .ndo_fix_features = qeth_l3_fix_features, - .ndo_set_features = qeth_l3_set_features, - .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid, + .ndo_set_rx_mode = qeth_l3_set_rx_mode, + .ndo_eth_ioctl = qeth_do_ioctl, + .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate, + .ndo_fix_features = qeth_fix_features, + .ndo_set_features = qeth_set_features, .ndo_tx_timeout = qeth_tx_timeout, .ndo_neigh_setup = qeth_l3_neigh_setup, }; static int qeth_l3_setup_netdev(struct qeth_card *card) { - if (card->info.type == QETH_CARD_TYPE_OSD || - card->info.type == QETH_CARD_TYPE_OSX) { - if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || - (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; - if (!card->info.guestlan) { - card->dev->hw_features = NETIF_F_SG | - NETIF_F_RXCSUM | NETIF_F_IP_CSUM | - NETIF_F_TSO; - card->dev->features = NETIF_F_RXCSUM; - } + struct net_device *dev = card->dev; + unsigned int headroom; + int rc; + + if (IS_OSD(card) || IS_OSX(card)) { + card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; + + /*IPv6 address autoconfiguration stuff*/ + dev->dev_id = qeth_l3_get_unique_id(card, dev->dev_id); + + if (!IS_VM_NIC(card)) { + 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; + } + if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) { + card->dev->hw_features |= NETIF_F_TSO6; + card->dev->vlan_features |= NETIF_F_TSO6; } - } else if (card->info.type == QETH_CARD_TYPE_IQD) { - card->dev = alloc_netdev(0, "hsi%d", ether_setup); - if (!card->dev) - return -ENODEV; + + /* allow for de-acceleration of NETIF_F_HW_VLAN_CTAG_TX: */ + if (card->dev->hw_features & NETIF_F_TSO6) + headroom = sizeof(struct qeth_hdr_tso) + VLAN_HLEN; + else if (card->dev->hw_features & NETIF_F_TSO) + headroom = sizeof(struct qeth_hdr_tso); + else + headroom = sizeof(struct qeth_hdr) + VLAN_HLEN; + } else if (IS_IQD(card)) { card->dev->flags |= IFF_NOARP; card->dev->netdev_ops = &qeth_l3_netdev_ops; - qeth_l3_iqd_read_initial_mac(card); - if (card->options.hsuid[0]) - memcpy(card->dev->perm_addr, card->options.hsuid, 9); + headroom = sizeof(struct qeth_hdr) - ETH_HLEN; + + rc = qeth_l3_iqd_read_initial_mac(card); + if (rc) + return rc; } else return -ENODEV; - card->dev->ml_priv = card; - card->dev->watchdog_timeo = QETH_TX_TIMEOUT; - card->dev->mtu = card->info.initial_mtu; - SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops); + card->dev->needed_headroom = headroom; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_VLAN_CTAG_FILTER; - card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; - card->dev->gso_max_size = 15 * PAGE_SIZE; + NETIF_F_HW_VLAN_CTAG_RX; - SET_NETDEV_DEV(card->dev, &card->gdev->dev); - netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT); + netif_keep_dst(card->dev); + if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) + netif_set_tso_max_size(card->dev, + PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1)); + + netif_napi_add(card->dev, &card->napi, qeth_poll); return register_netdev(card->dev); } +static const struct device_type qeth_l3_devtype = { + .name = "qeth_layer3", + .groups = qeth_l3_attr_groups, +}; + static int qeth_l3_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); + int rc; + + hash_init(card->ip_htable); + mutex_init(&card->ip_lock); + card->cmd_wq = alloc_ordered_workqueue("%s_cmd", 0, + dev_name(&gdev->dev)); + if (!card->cmd_wq) + return -ENOMEM; + + if (gdev->dev.type) { + rc = device_add_groups(&gdev->dev, qeth_l3_attr_groups); + if (rc) { + destroy_workqueue(card->cmd_wq); + return rc; + } + } else { + gdev->dev.type = &qeth_l3_devtype; + } - qeth_l3_create_device_attributes(&gdev->dev); - card->options.layer2 = 0; - card->info.hwtrap = 0; + INIT_WORK(&card->rx_mode_work, qeth_l3_rx_mode_work); return 0; } @@ -3324,330 +1953,191 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) { struct qeth_card *card = dev_get_drvdata(&cgdev->dev); - qeth_l3_remove_device_attributes(&cgdev->dev); + if (cgdev->dev.type != &qeth_l3_devtype) + device_remove_groups(&cgdev->dev, qeth_l3_attr_groups); qeth_set_allowed_threads(card, 0, 1); wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); if (cgdev->state == CCWGROUP_ONLINE) - qeth_l3_set_offline(cgdev); + qeth_set_offline(card, card->discipline, false); - if (card->dev) { + if (card->dev->reg_state == NETREG_REGISTERED) unregister_netdev(card->dev); - card->dev = NULL; - } - qeth_l3_clear_ip_list(card, 0); + destroy_workqueue(card->cmd_wq); + qeth_l3_clear_ip_htable(card, 0); qeth_l3_clear_ipato_list(card); - return; } -static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) +static int qeth_l3_set_online(struct qeth_card *card, bool carrier_ok) { - struct qeth_card *card = dev_get_drvdata(&gdev->dev); + struct net_device *dev = card->dev; int rc = 0; - enum qeth_card_states recover_flag; - - mutex_lock(&card->discipline_mutex); - mutex_lock(&card->conf_mutex); - QETH_DBF_TEXT(SETUP, 2, "setonlin"); - QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); - - recover_flag = card->state; - rc = qeth_core_hardsetup_card(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); - rc = -ENODEV; - goto out_remove; - } - - if (!card->dev && qeth_l3_setup_netdev(card)) { - rc = -ENODEV; - goto out_remove; - } - - if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { - if (card->info.hwtrap && - qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) - card->info.hwtrap = 0; - } else - card->info.hwtrap = 0; - - card->state = CARD_STATE_HARDSETUP; - memset(&card->rx, 0, sizeof(struct qeth_rx)); - qeth_print_status_message(card); /* softsetup */ - QETH_DBF_TEXT(SETUP, 2, "softsetp"); - - rc = qeth_send_startlan(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (rc == 0xe080) { - dev_warn(&card->gdev->dev, - "The LAN is offline\n"); - card->lan_online = 0; - goto contin; - } - rc = -ENODEV; - goto out_remove; - } else - card->lan_online = 1; + QETH_CARD_TEXT(card, 2, "softsetp"); -contin: rc = qeth_l3_setadapter_parms(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + QETH_CARD_TEXT_(card, 2, "2err%04x", rc); if (!card->options.sniffer) { - rc = qeth_l3_start_ipassists(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); - goto out_remove; - } + qeth_l3_start_ipassists(card); + rc = qeth_l3_setrouting_v4(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); + QETH_CARD_TEXT_(card, 2, "4err%04x", rc); rc = qeth_l3_setrouting_v6(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); + QETH_CARD_TEXT_(card, 2, "5err%04x", rc); } - netif_tx_disable(card->dev); - rc = qeth_init_qdio_queues(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); - rc = -ENODEV; - goto out_remove; - } card->state = CARD_STATE_SOFTSETUP; qeth_set_allowed_threads(card, 0xffffffff, 0); - qeth_l3_set_ip_addr_list(card); - if (card->lan_online) - netif_carrier_on(card->dev); - else - netif_carrier_off(card->dev); - if (recover_flag == CARD_STATE_RECOVER) { + qeth_l3_recover_ip(card); + + if (dev->reg_state != NETREG_REGISTERED) { + rc = qeth_l3_setup_netdev(card); + if (rc) + goto err_setup; + + if (carrier_ok) + netif_carrier_on(dev); + } else { rtnl_lock(); - if (recovery_mode) - __qeth_l3_open(card->dev); + rc = qeth_set_real_num_tx_queues(card, + qeth_tx_actual_queues(card)); + if (rc) { + rtnl_unlock(); + goto err_set_queues; + } + + if (carrier_ok) + netif_carrier_on(dev); else - dev_open(card->dev); - qeth_l3_set_multicast_list(card->dev); + netif_carrier_off(dev); + + netif_device_attach(dev); + qeth_enable_hw_features(dev); + + if (netif_running(dev)) { + local_bh_disable(); + napi_schedule(&card->napi); + /* kick-start the NAPI softirq: */ + local_bh_enable(); + } rtnl_unlock(); } - qeth_trace_features(card); - /* let user_space know that device is online */ - kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); - mutex_unlock(&card->conf_mutex); - mutex_unlock(&card->discipline_mutex); return 0; -out_remove: - qeth_l3_stop_card(card, 0); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); - if (recover_flag == CARD_STATE_RECOVER) - card->state = CARD_STATE_RECOVER; - else - card->state = CARD_STATE_DOWN; - mutex_unlock(&card->conf_mutex); - mutex_unlock(&card->discipline_mutex); + +err_set_queues: +err_setup: + qeth_set_allowed_threads(card, 0, 1); + card->state = CARD_STATE_DOWN; + qeth_l3_clear_ip_htable(card, 1); return rc; } -static int qeth_l3_set_online(struct ccwgroup_device *gdev) +static void qeth_l3_set_offline(struct qeth_card *card) { - return __qeth_l3_set_online(gdev, 0); -} + qeth_set_allowed_threads(card, 0, 1); + qeth_l3_drain_rx_mode_cache(card); -static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, - int recovery_mode) -{ - struct qeth_card *card = dev_get_drvdata(&cgdev->dev); - int rc = 0, rc2 = 0, rc3 = 0; - enum qeth_card_states recover_flag; - - mutex_lock(&card->discipline_mutex); - mutex_lock(&card->conf_mutex); - QETH_DBF_TEXT(SETUP, 3, "setoffl"); - QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - - if (card->dev && netif_carrier_ok(card->dev)) - netif_carrier_off(card->dev); - recover_flag = card->state; - if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { - qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); - card->info.hwtrap = 1; - } - qeth_l3_stop_card(card, recovery_mode); - if ((card->options.cq == QETH_CQ_ENABLED) && card->dev) { - rtnl_lock(); - call_netdevice_notifiers(NETDEV_REBOOT, card->dev); - rtnl_unlock(); + if (card->options.sniffer && + (card->info.promisc_mode == SET_PROMISC_MODE_ON)) + qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE); + + if (card->state == CARD_STATE_SOFTSETUP) { + card->state = CARD_STATE_DOWN; + qeth_l3_clear_ip_htable(card, 1); } - rc = ccw_device_set_offline(CARD_DDEV(card)); - rc2 = ccw_device_set_offline(CARD_WDEV(card)); - rc3 = ccw_device_set_offline(CARD_RDEV(card)); - if (!rc) - rc = (rc2) ? rc2 : rc3; - if (rc) - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (recover_flag == CARD_STATE_UP) - card->state = CARD_STATE_RECOVER; - /* let user_space know that device is offline */ - kobject_uevent(&cgdev->dev.kobj, KOBJ_CHANGE); - mutex_unlock(&card->conf_mutex); - mutex_unlock(&card->discipline_mutex); - return 0; } -static int qeth_l3_set_offline(struct ccwgroup_device *cgdev) +/* Returns zero if the command is successfully "consumed" */ +static int qeth_l3_control_event(struct qeth_card *card, + struct qeth_ipa_cmd *cmd) { - return __qeth_l3_set_offline(cgdev, 0); + return 1; } -static int qeth_l3_recover(void *ptr) -{ - struct qeth_card *card; - int rc = 0; +const struct qeth_discipline qeth_l3_discipline = { + .setup = qeth_l3_probe_device, + .remove = qeth_l3_remove_device, + .set_online = qeth_l3_set_online, + .set_offline = qeth_l3_set_offline, + .control_event_handler = qeth_l3_control_event, +}; +EXPORT_SYMBOL_GPL(qeth_l3_discipline); - card = (struct qeth_card *) ptr; - QETH_CARD_TEXT(card, 2, "recover1"); - QETH_CARD_HEX(card, 2, &card, sizeof(void *)); - if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD)) - return 0; - QETH_CARD_TEXT(card, 2, "recover2"); - dev_warn(&card->gdev->dev, - "A recovery process has been started for the device\n"); - qeth_set_recovery_task(card); - __qeth_l3_set_offline(card->gdev, 1); - rc = __qeth_l3_set_online(card->gdev, 1); - if (!rc) - dev_info(&card->gdev->dev, - "Device successfully recovered!\n"); - else { - qeth_close_dev(card); - dev_warn(&card->gdev->dev, "The qeth device driver " - "failed to recover an error on the device\n"); +static int qeth_l3_handle_ip_event(struct qeth_card *card, + struct qeth_ipaddr *addr, + unsigned long event) +{ + switch (event) { + case NETDEV_UP: + qeth_l3_modify_ip(card, addr, true); + return NOTIFY_OK; + case NETDEV_DOWN: + qeth_l3_modify_ip(card, addr, false); + return NOTIFY_OK; + default: + return NOTIFY_DONE; } - qeth_clear_recovery_task(card); - qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); - qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); - return 0; } -static void qeth_l3_shutdown(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - qeth_set_allowed_threads(card, 0, 1); - if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) - qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); - qeth_qdio_clear_card(card, 0); - qeth_clear_qdio_buffers(card); -} +struct qeth_l3_ip_event_work { + struct work_struct work; + struct qeth_card *card; + struct qeth_ipaddr addr; +}; -static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) +#define to_ip_work(w) container_of((w), struct qeth_l3_ip_event_work, work) + +static void qeth_l3_add_ip_worker(struct work_struct *work) { - struct qeth_card *card = dev_get_drvdata(&gdev->dev); + struct qeth_l3_ip_event_work *ip_work = to_ip_work(work); - if (card->dev) - netif_device_detach(card->dev); - qeth_set_allowed_threads(card, 0, 1); - wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); - if (gdev->state == CCWGROUP_OFFLINE) - return 0; - if (card->state == CARD_STATE_UP) { - if (card->info.hwtrap) - qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); - __qeth_l3_set_offline(card->gdev, 1); - } else - __qeth_l3_set_offline(card->gdev, 0); - return 0; + qeth_l3_modify_ip(ip_work->card, &ip_work->addr, true); + kfree(work); } -static int qeth_l3_pm_resume(struct ccwgroup_device *gdev) +static void qeth_l3_delete_ip_worker(struct work_struct *work) { - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - int rc = 0; - - if (gdev->state == CCWGROUP_OFFLINE) - goto out; + struct qeth_l3_ip_event_work *ip_work = to_ip_work(work); - if (card->state == CARD_STATE_RECOVER) { - rc = __qeth_l3_set_online(card->gdev, 1); - if (rc) { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); - } - } else - rc = __qeth_l3_set_online(card->gdev, 0); -out: - qeth_set_allowed_threads(card, 0xffffffff, 0); - if (card->dev) - netif_device_attach(card->dev); - if (rc) - dev_warn(&card->gdev->dev, "The qeth device driver " - "failed to recover an error on the device\n"); - return rc; + qeth_l3_modify_ip(ip_work->card, &ip_work->addr, false); + kfree(work); } -struct qeth_discipline qeth_l3_discipline = { - .start_poll = qeth_qdio_start_poll, - .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, - .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, - .recover = qeth_l3_recover, - .setup = qeth_l3_probe_device, - .remove = qeth_l3_remove_device, - .set_online = qeth_l3_set_online, - .set_offline = qeth_l3_set_offline, - .shutdown = qeth_l3_shutdown, - .freeze = qeth_l3_pm_suspend, - .thaw = qeth_l3_pm_resume, - .restore = qeth_l3_pm_resume, -}; -EXPORT_SYMBOL_GPL(qeth_l3_discipline); +static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) +{ + if (is_vlan_dev(dev)) + dev = vlan_dev_real_dev(dev); + if (dev->netdev_ops == &qeth_l3_osa_netdev_ops || + dev->netdev_ops == &qeth_l3_netdev_ops) + return (struct qeth_card *) dev->ml_priv; + return NULL; +} static int qeth_l3_ip_event(struct notifier_block *this, unsigned long event, void *ptr) { struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; - struct net_device *dev = (struct net_device *)ifa->ifa_dev->dev; - struct qeth_ipaddr *addr; + struct net_device *dev = ifa->ifa_dev->dev; + struct qeth_ipaddr addr; struct qeth_card *card; - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - card = qeth_l3_get_card_from_dev(dev); if (!card) return NOTIFY_DONE; QETH_CARD_TEXT(card, 3, "ipevent"); - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (addr != NULL) { - addr->u.a4.addr = ifa->ifa_address; - addr->u.a4.mask = ifa->ifa_mask; - addr->type = QETH_IP_TYPE_NORMAL; - } else - goto out; + qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); + addr.u.a4.addr = ifa->ifa_address; + addr.u.a4.mask = ifa->ifa_mask; - switch (event) { - case NETDEV_UP: - if (!qeth_l3_add_ip(card, addr)) - kfree(addr); - break; - case NETDEV_DOWN: - if (!qeth_l3_delete_ip(card, addr)) - kfree(addr); - break; - default: - break; - } - qeth_l3_set_ip_addr_list(card); -out: - return NOTIFY_DONE; + return qeth_l3_handle_ip_event(card, &addr, event); } static struct notifier_block qeth_l3_ip_notifier = { @@ -3655,18 +2145,17 @@ static struct notifier_block qeth_l3_ip_notifier = { NULL, }; -#ifdef CONFIG_QETH_IPV6 -/** - * IPv6 event handler - */ static int qeth_l3_ip6_event(struct notifier_block *this, unsigned long event, void *ptr) { struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; - struct net_device *dev = (struct net_device *)ifa->idev->dev; - struct qeth_ipaddr *addr; + struct net_device *dev = ifa->idev->dev; + struct qeth_l3_ip_event_work *ip_work; struct qeth_card *card; + if (event != NETDEV_UP && event != NETDEV_DOWN) + return NOTIFY_DONE; + card = qeth_l3_get_card_from_dev(dev); if (!card) return NOTIFY_DONE; @@ -3674,36 +2163,29 @@ static int qeth_l3_ip6_event(struct notifier_block *this, if (!qeth_is_supported(card, IPA_IPV6)) return NOTIFY_DONE; - addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); - if (addr != NULL) { - memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr)); - addr->u.a6.pfxlen = ifa->prefix_len; - addr->type = QETH_IP_TYPE_NORMAL; - } else - goto out; + ip_work = kmalloc(sizeof(*ip_work), GFP_ATOMIC); + if (!ip_work) + return NOTIFY_DONE; - switch (event) { - case NETDEV_UP: - if (!qeth_l3_add_ip(card, addr)) - kfree(addr); - break; - case NETDEV_DOWN: - if (!qeth_l3_delete_ip(card, addr)) - kfree(addr); - break; - default: - break; - } - qeth_l3_set_ip_addr_list(card); -out: - return NOTIFY_DONE; + if (event == NETDEV_UP) + INIT_WORK(&ip_work->work, qeth_l3_add_ip_worker); + else + INIT_WORK(&ip_work->work, qeth_l3_delete_ip_worker); + + ip_work->card = card; + qeth_l3_init_ipaddr(&ip_work->addr, QETH_IP_TYPE_NORMAL, + QETH_PROT_IPV6); + ip_work->addr.u.a6.addr = ifa->addr; + ip_work->addr.u.a6.pfxlen = ifa->prefix_len; + + queue_work(card->cmd_wq, &ip_work->work); + return NOTIFY_OK; } static struct notifier_block qeth_l3_ip6_notifier = { qeth_l3_ip6_event, NULL, }; -#endif static int qeth_l3_register_notifiers(void) { @@ -3713,35 +2195,25 @@ static int qeth_l3_register_notifiers(void) rc = register_inetaddr_notifier(&qeth_l3_ip_notifier); if (rc) return rc; -#ifdef CONFIG_QETH_IPV6 rc = register_inet6addr_notifier(&qeth_l3_ip6_notifier); if (rc) { unregister_inetaddr_notifier(&qeth_l3_ip_notifier); return rc; } -#else - pr_warning("There is no IPv6 support for the layer 3 discipline\n"); -#endif return 0; } static void qeth_l3_unregister_notifiers(void) { - QETH_DBF_TEXT(SETUP, 5, "unregnot"); WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier)); -#ifdef CONFIG_QETH_IPV6 WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier)); -#endif /* QETH_IPV6 */ } static int __init qeth_l3_init(void) { - int rc = 0; - pr_info("register layer 3 discipline\n"); - rc = qeth_l3_register_notifiers(); - return rc; + return qeth_l3_register_notifiers(); } static void __exit qeth_l3_exit(void) |
