diff options
author | Lakhvich Dmitriy <LDmitriy@ru.ibm.com> | 2016-06-16 16:18:58 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-06-16 22:16:12 -0700 |
commit | 5f78e29ceebf03a80a5141515bd5b48ca83f0495 (patch) | |
tree | e4a10605c77cb9217d9cea58e9fdaeeea3d87f09 /drivers | |
parent | 6059c90537868bfd365163ba3dff95cf775939ff (diff) |
qeth: optimize IP handling in rx_mode callback
In layer3 mode of the qeth driver, multicast IP addresses
from struct net_device and other type of IP addresses
from other sources require mapping to the OSA-card.
This patch simplifies the IP address mapping logic, and changes imple-
mentation of ndo_set_rx_mode callback and ip notifier events.
Addresses are stored in private hashtables instead of lists now.
It allows hardware registration/removal for new/deleted multicast
addresses only.
Signed-off-by: Lakhvich Dmitriy <ldmitriy@ru.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Reviewed-by: Evgeny Cherkashin <Eugene.Crosser@ru.ibm.com>
Reviewed-by: Thomas Richter <tmricht@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/net/qeth_core.h | 20 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 10 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2.h | 7 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 12 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3.h | 31 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 782 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_sys.c | 78 |
7 files changed, 482 insertions, 458 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index b4f14e4b8966..ab0a171e7979 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -561,7 +561,6 @@ enum qeth_ip_types { QETH_IP_TYPE_NORMAL, QETH_IP_TYPE_VIPA, QETH_IP_TYPE_RXIP, - QETH_IP_TYPE_DEL_ALL_MC, }; enum qeth_cmd_buffer_state { @@ -742,17 +741,10 @@ struct qeth_vlan_vid { unsigned short vid; }; -enum qeth_mac_disposition { - QETH_DISP_MAC_DELETE = 0, - QETH_DISP_MAC_DO_NOTHING = 1, - QETH_DISP_MAC_ADD = 2, -}; - -struct qeth_mac { - u8 mac_addr[OSA_ADDR_LEN]; - u8 is_uc:1; - u8 disp_flag:2; - struct hlist_node hnode; +enum qeth_addr_disposition { + QETH_DISP_ADDR_DELETE = 0, + QETH_DISP_ADDR_DO_NOTHING = 1, + QETH_DISP_ADDR_ADD = 2, }; struct qeth_rx { @@ -800,6 +792,8 @@ struct qeth_card { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct list_head vid_list; DECLARE_HASHTABLE(mac_htable, 4); + DECLARE_HASHTABLE(ip_htable, 4); + DECLARE_HASHTABLE(ip_mc_htable, 4); struct work_struct kernel_thread_starter; spinlock_t thread_mask_lock; unsigned long thread_start_mask; @@ -807,8 +801,6 @@ struct qeth_card { unsigned long thread_running_mask; struct task_struct *recovery_task; spinlock_t ip_lock; - struct list_head ip_list; - struct list_head *ip_tbd_list; struct qeth_ipato ipato; struct list_head cmd_waiter_list; /* QDIO buffer handling */ diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9806ee046fa5..ede9ed8afcef 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1464,8 +1464,6 @@ static int qeth_setup_card(struct qeth_card *card) card->thread_allowed_mask = 0; card->thread_running_mask = 0; INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); - INIT_LIST_HEAD(&card->ip_list); - INIT_LIST_HEAD(card->ip_tbd_list); INIT_LIST_HEAD(&card->cmd_waiter_list); init_waitqueue_head(&card->wait_q); /* initial options */ @@ -1500,11 +1498,6 @@ static struct qeth_card *qeth_alloc_card(void) if (!card) goto out; QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); - card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); - if (!card->ip_tbd_list) { - QETH_DBF_TEXT(SETUP, 0, "iptbdnom"); - goto out_card; - } if (qeth_setup_channel(&card->read)) goto out_ip; if (qeth_setup_channel(&card->write)) @@ -1517,8 +1510,6 @@ static struct qeth_card *qeth_alloc_card(void) out_channel: qeth_clean_channel(&card->read); out_ip: - kfree(card->ip_tbd_list); -out_card: kfree(card); out: return NULL; @@ -4980,7 +4971,6 @@ static void qeth_core_free_card(struct qeth_card *card) qeth_clean_channel(&card->write); if (card->dev) free_netdev(card->dev); - kfree(card->ip_tbd_list); qeth_free_qdio_buffers(card); unregister_service_level(&card->qeth_service_level); kfree(card); diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h index 0767556404bd..29d9fb3890ad 100644 --- a/drivers/s390/net/qeth_l2.h +++ b/drivers/s390/net/qeth_l2.h @@ -12,4 +12,11 @@ int qeth_l2_create_device_attributes(struct device *); void qeth_l2_remove_device_attributes(struct device *); void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); +struct qeth_mac { + u8 mac_addr[OSA_ADDR_LEN]; + u8 is_uc:1; + u8 disp_flag:2; + struct hlist_node hnode; +}; + #endif /* __QETH_L2_H__ */ diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 08b5fa969d4a..2a331d163f99 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -780,7 +780,7 @@ qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) qeth_l2_mac_hash(ha->addr)) { if (is_uc == mac->is_uc && !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) { - mac->disp_flag = QETH_DISP_MAC_DO_NOTHING; + mac->disp_flag = QETH_DISP_ADDR_DO_NOTHING; return; } } @@ -792,7 +792,7 @@ qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN); mac->is_uc = is_uc; - mac->disp_flag = QETH_DISP_MAC_ADD; + mac->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->mac_htable, &mac->hnode, qeth_l2_mac_hash(mac->mac_addr)); @@ -825,7 +825,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) qeth_l2_add_mac(card, ha, 1); hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { - if (mac->disp_flag == QETH_DISP_MAC_DELETE) { + if (mac->disp_flag == QETH_DISP_ADDR_DELETE) { if (!mac->is_uc) rc = qeth_l2_send_delgroupmac(card, mac->mac_addr); @@ -837,15 +837,15 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) hash_del(&mac->hnode); kfree(mac); - } else if (mac->disp_flag == QETH_DISP_MAC_ADD) { + } else if (mac->disp_flag == QETH_DISP_ADDR_ADD) { rc = qeth_l2_write_mac(card, mac); if (rc) { hash_del(&mac->hnode); kfree(mac); } else - mac->disp_flag = QETH_DISP_MAC_DELETE; + mac->disp_flag = QETH_DISP_ADDR_DELETE; } else - mac->disp_flag = QETH_DISP_MAC_DELETE; + mac->disp_flag = QETH_DISP_ADDR_DELETE; } spin_unlock_bh(&card->mclock); diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 551a4b4c03fd..26f79533e62e 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -10,16 +10,23 @@ #define __QETH_L3_H__ #include "qeth_core.h" +#include <linux/hashtable.h> #define QETH_SNIFF_AVAIL 0x0008 struct qeth_ipaddr { - struct list_head entry; + struct hlist_node hnode; enum qeth_ip_types type; enum qeth_ipa_setdelip_flags set_flags; enum qeth_ipa_setdelip_flags del_flags; - int is_multicast; - int users; + u8 is_multicast:1; + u8 in_progress:1; + u8 disp_flag:2; + + /* is changed only for normal ip addresses + * for non-normal addresses it always is 1 + */ + int ref_counter; enum qeth_prot_versions proto; unsigned char mac[OSA_ADDR_LEN]; union { @@ -32,7 +39,24 @@ struct qeth_ipaddr { unsigned int pfxlen; } a6; } u; + }; +static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) +{ + u64 ret = 0; + u8 *point; + + if (addr->proto == QETH_PROT_IPV6) { + point = (u8 *) &addr->u.a6.addr; + ret = get_unaligned((u64 *)point) ^ + get_unaligned((u64 *) (point + 8)); + } + if (addr->proto == QETH_PROT_IPV4) { + point = (u8 *) &addr->u.a4.addr; + ret = get_unaligned((u32 *) point); + } + return ret; +} struct qeth_ipato_entry { struct list_head entry; @@ -60,6 +84,5 @@ int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *); struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions); int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *); int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *); -void qeth_l3_set_ip_addr_list(struct qeth_card *); #endif /* __QETH_L3_H__ */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 51077fb69a98..6e7d06cfa7a8 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -30,6 +30,7 @@ #include <net/ip6_fib.h> #include <net/ip6_checksum.h> #include <net/iucv/af_iucv.h> +#include <linux/hashtable.h> #include "qeth_l3.h" @@ -57,7 +58,7 @@ static int qeth_l3_isxdigit(char *buf) static 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]); + sprintf(buf, "%pI4", addr); } static int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr) @@ -204,104 +205,129 @@ 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) +inline int +qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2) { - struct qeth_ipaddr *tmp, *t; - int found = 0; + return addr1->proto == addr2->proto && + !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) && + !memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac)); +} - 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; +static struct qeth_ipaddr * +qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) +{ + struct qeth_ipaddr *addr; + + if (tmp_addr->is_multicast) { + hash_for_each_possible(card->ip_mc_htable, addr, + hnode, qeth_l3_ipaddr_hash(tmp_addr)) + if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr)) + return addr; } 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; + hash_for_each_possible(card->ip_htable, addr, + hnode, qeth_l3_ipaddr_hash(tmp_addr)) + if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr)) + return addr; } + + return NULL; } -int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +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 (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); + + addr = qeth_l3_ip_from_hash(card, tmp_addr); + if (!addr) + return -ENOENT; + + addr->ref_counter--; + if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0) + return rc; + if (addr->in_progress) + return -EINPROGRESS; + + rc = qeth_l3_deregister_addr_entry(card, addr); + + hash_del(&addr->hnode); + kfree(addr); + return rc; } -int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +int qeth_l3_add_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, "addip"); - 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); + } + + addr = qeth_l3_ip_from_hash(card, tmp_addr); + if (!addr) { + addr = qeth_l3_get_addr_buffer(tmp_addr->proto); + if (!addr) + return -ENOMEM; + + memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr)); + addr->ref_counter = 1; + + if (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; + } + hash_add(card->ip_htable, &addr->hnode, + qeth_l3_ipaddr_hash(addr)); + + /* qeth_l3_register_addr_entry can go to sleep + * if we add a IPV4 addr. It is caused by the reason + * that SETIP ipa cmd starts ARP staff for IPV4 addr. + * Thus we should unlock spinlock, and make a protection + * using in_progress variable to indicate that there is + * an hardware operation with this IPV4 address + */ + if (addr->proto == QETH_PROT_IPV4) { + addr->in_progress = 1; + spin_unlock_bh(&card->ip_lock); + rc = qeth_l3_register_addr_entry(card, addr); + spin_lock_bh(&card->ip_lock); + addr->in_progress = 0; + } else + rc = qeth_l3_register_addr_entry(card, addr); + + if (!rc || (rc == IPA_RC_DUPLICATE_IP_ADDRESS) || + (rc == IPA_RC_LAN_OFFLINE)) { + addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + if (addr->ref_counter < 1) { + qeth_l3_delete_ip(card, addr); + kfree(addr); + } + } else { + hash_del(&addr->hnode); + kfree(addr); + } + } else { + if (addr->type == QETH_IP_TYPE_NORMAL) + addr->ref_counter++; } - 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; } @@ -312,229 +338,88 @@ struct qeth_ipaddr *qeth_l3_get_addr_buffer( struct qeth_ipaddr *addr; addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC); - if (addr == NULL) { + if (!addr) return NULL; - } + addr->type = QETH_IP_TYPE_NORMAL; + addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; addr->proto = prot; - return addr; -} -static void qeth_l3_delete_mc_addresses(struct qeth_card *card) -{ - struct qeth_ipaddr *iptodo; - unsigned long flags; - - QETH_CARD_TEXT(card, 4, "delmc"); - iptodo = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!iptodo) { - QETH_CARD_TEXT(card, 2, "dmcnomem"); - return; - } - 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); + return addr; } -/* - * 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) +static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover) { 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; - } - 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; - } - } - 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; - 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; - - 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; - } - } - list_splice(&fail_list, &card->ip_list); -} - -void qeth_l3_set_ip_addr_list(struct qeth_card *card) -{ - struct list_head *tbd_list; - struct qeth_ipaddr *todo, *addr; - unsigned long flags; - int rc; + struct hlist_node *tmp; + int i; - QETH_CARD_TEXT(card, 2, "sdiplist"); - QETH_CARD_HEX(card, 2, &card, sizeof(void *)); + QETH_CARD_TEXT(card, 4, "clearip"); - if (!qeth_card_hw_is_reachable(card) || card->options.sniffer) + if (recover && card->options.sniffer) return; - 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); + spin_lock_bh(&card->ip_lock); + + hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { + if (!recover) { + hash_del(&addr->hnode); + kfree(addr); 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); - } + addr->disp_flag = QETH_DISP_ADDR_ADD; } - spin_unlock_irqrestore(&card->ip_lock, flags); - kfree(tbd_list); -} -static void qeth_l3_clear_ip_list(struct qeth_card *card, int recover) -{ - struct qeth_ipaddr *addr, *tmp; - unsigned long flags; + spin_unlock_bh(&card->ip_lock); - 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); + spin_lock_bh(&card->mclock); + + hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { + hash_del(&addr->hnode); 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) { - kfree(addr); - continue; - } - list_add_tail(&addr->entry, card->ip_tbd_list); - } - spin_unlock_irqrestore(&card->ip_lock, flags); -} + spin_unlock_bh(&card->mclock); + -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; - 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; + QETH_CARD_TEXT(card, 4, "recoverip"); + + spin_lock_bh(&card->ip_lock); + + hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { + if (addr->disp_flag == QETH_DISP_ADDR_ADD) { + if (addr->proto == QETH_PROT_IPV4) { + addr->in_progress = 1; + spin_unlock_bh(&card->ip_lock); + rc = qeth_l3_register_addr_entry(card, addr); + spin_lock_bh(&card->ip_lock); + addr->in_progress = 0; + } else + rc = qeth_l3_register_addr_entry(card, addr); + + if (!rc) { + addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + if (addr->ref_counter < 1) { + qeth_l3_delete_ip(card, addr); + kfree(addr); + } + } else { + hash_del(&addr->hnode); + kfree(addr); + } + } + } - 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; + spin_unlock_bh(&card->ip_lock); - } - return 0; } static int qeth_l3_send_setdelmc(struct qeth_card *card, @@ -712,27 +597,28 @@ int qeth_l3_setrouting_v6(struct qeth_card *card) */ 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); + spin_lock_bh(&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); + + spin_unlock_bh(&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); + + spin_lock_bh(&card->ip_lock); + list_for_each_entry(ipatoe, &card->ipato.entries, entry) { if (ipatoe->proto != new->proto) continue; @@ -743,10 +629,12 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, break; } } + if (!rc) list_add_tail(&new->entry, &card->ipato.entries); - spin_unlock_irqrestore(&card->ip_lock, flags); + spin_unlock_bh(&card->ip_lock); + return rc; } @@ -754,10 +642,11 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card, enum qeth_prot_versions proto, u8 *addr, int mask_bits) { struct qeth_ipato_entry *ipatoe, *tmp; - unsigned long flags; QETH_CARD_TEXT(card, 2, "delipato"); - spin_lock_irqsave(&card->ip_lock, flags); + + spin_lock_bh(&card->ip_lock); + list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { if (ipatoe->proto != proto) continue; @@ -768,7 +657,8 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card, kfree(ipatoe); } } - spin_unlock_irqrestore(&card->ip_lock, flags); + + spin_unlock_bh(&card->ip_lock); } /* @@ -778,7 +668,6 @@ 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; ipaddr = qeth_l3_get_addr_buffer(proto); @@ -797,18 +686,18 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto, 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)) + + spin_lock_bh(&card->ip_lock); + + if (!qeth_l3_ip_from_hash(card, ipaddr)) 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); + else + qeth_l3_add_ip(card, ipaddr); + + spin_unlock_bh(&card->ip_lock); + + kfree(ipaddr); + return rc; } @@ -831,9 +720,12 @@ void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto, ipaddr->type = QETH_IP_TYPE_VIPA; } else return; - if (!qeth_l3_delete_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); + + spin_lock_bh(&card->ip_lock); + qeth_l3_delete_ip(card, ipaddr); + spin_unlock_bh(&card->ip_lock); + + kfree(ipaddr); } /* @@ -843,7 +735,6 @@ 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; ipaddr = qeth_l3_get_addr_buffer(proto); @@ -857,24 +748,25 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto, 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)) + + spin_lock_bh(&card->ip_lock); + + if (!qeth_l3_ip_from_hash(card, ipaddr)) 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; + else + qeth_l3_add_ip(card, ipaddr); + + spin_unlock_bh(&card->ip_lock); + + kfree(ipaddr); + + return rc; } void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto, @@ -896,9 +788,12 @@ void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto, ipaddr->type = QETH_IP_TYPE_RXIP; } else return; - if (!qeth_l3_delete_ip(card, ipaddr)) - kfree(ipaddr); - qeth_l3_set_ip_addr_list(card); + + spin_lock_bh(&card->ip_lock); + qeth_l3_delete_ip(card, ipaddr); + spin_unlock_bh(&card->ip_lock); + + kfree(ipaddr); } static int qeth_l3_register_addr_entry(struct qeth_card *card, @@ -908,6 +803,7 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card, int rc = 0; int cnt = 3; + if (addr->proto == QETH_PROT_IPV4) { QETH_CARD_TEXT(card, 2, "setaddr4"); QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); @@ -1507,31 +1403,99 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds 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) +static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac) { ip_eth_mc_map(ipm, mac); } -static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) +static void qeth_l3_mark_all_mc_to_be_deleted(struct qeth_card *card) +{ + struct qeth_ipaddr *addr; + int i; + + hash_for_each(card->ip_mc_htable, i, addr, hnode) + addr->disp_flag = QETH_DISP_ADDR_DELETE; + +} + +static void qeth_l3_add_all_new_mc(struct qeth_card *card) +{ + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i; + int rc; + + hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { + if (addr->disp_flag == QETH_DISP_ADDR_ADD) { + rc = qeth_l3_register_addr_entry(card, addr); + if (!rc || (rc == IPA_RC_LAN_OFFLINE)) + addr->ref_counter = 1; + else { + hash_del(&addr->hnode); + kfree(addr); + } + } + } + +} + +static void qeth_l3_delete_nonused_mc(struct qeth_card *card) +{ + struct qeth_ipaddr *addr; + struct hlist_node *tmp; + int i; + int rc; + + hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { + if (addr->disp_flag == QETH_DISP_ADDR_DELETE) { + rc = qeth_l3_deregister_addr_entry(card, addr); + if (!rc || (rc == IPA_RC_MC_ADDR_NOT_FOUND)) { + hash_del(&addr->hnode); + kfree(addr); + } + } + } + +} + + +static void +qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) { - struct qeth_ipaddr *ipm; struct ip_mc_list *im4; + struct qeth_ipaddr *tmp, *ipm; char buf[MAX_ADDR_LEN]; QETH_CARD_TEXT(card, 4, "addmc"); + + tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); + if (!tmp) + return; + 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); + qeth_l3_get_mac_for_ipm(im4->multiaddr, buf); + + tmp->u.a4.addr = im4->multiaddr; + memcpy(tmp->mac, buf, sizeof(tmp->mac)); + + ipm = qeth_l3_ip_from_hash(card, tmp); + if (ipm) { + ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + } else { + ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); + if (!ipm) + continue; + memcpy(ipm->mac, buf, sizeof(tmp->mac)); + ipm->u.a4.addr = im4->multiaddr; + ipm->is_multicast = 1; + ipm->disp_flag = QETH_DISP_ADDR_ADD; + hash_add(card->ip_mc_htable, + &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); + } } + + kfree(tmp); } /* called with rcu_read_lock */ @@ -1541,6 +1505,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) u16 vid; QETH_CARD_TEXT(card, 4, "addmcvl"); + |