Commit 5f78e29c authored by Lakhvich Dmitriy's avatar Lakhvich Dmitriy Committed by David S. Miller

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: default avatarLakhvich Dmitriy <ldmitriy@ru.ibm.com>
Signed-off-by: default avatarUrsula Braun <ubraun@linux.vnet.ibm.com>
Reviewed-by: default avatarEvgeny Cherkashin <Eugene.Crosser@ru.ibm.com>
Reviewed-by: default avatarThomas Richter <tmricht@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6059c905
...@@ -561,7 +561,6 @@ enum qeth_ip_types { ...@@ -561,7 +561,6 @@ enum qeth_ip_types {
QETH_IP_TYPE_NORMAL, QETH_IP_TYPE_NORMAL,
QETH_IP_TYPE_VIPA, QETH_IP_TYPE_VIPA,
QETH_IP_TYPE_RXIP, QETH_IP_TYPE_RXIP,
QETH_IP_TYPE_DEL_ALL_MC,
}; };
enum qeth_cmd_buffer_state { enum qeth_cmd_buffer_state {
...@@ -742,17 +741,10 @@ struct qeth_vlan_vid { ...@@ -742,17 +741,10 @@ struct qeth_vlan_vid {
unsigned short vid; unsigned short vid;
}; };
enum qeth_mac_disposition { enum qeth_addr_disposition {
QETH_DISP_MAC_DELETE = 0, QETH_DISP_ADDR_DELETE = 0,
QETH_DISP_MAC_DO_NOTHING = 1, QETH_DISP_ADDR_DO_NOTHING = 1,
QETH_DISP_MAC_ADD = 2, QETH_DISP_ADDR_ADD = 2,
};
struct qeth_mac {
u8 mac_addr[OSA_ADDR_LEN];
u8 is_uc:1;
u8 disp_flag:2;
struct hlist_node hnode;
}; };
struct qeth_rx { struct qeth_rx {
...@@ -800,6 +792,8 @@ struct qeth_card { ...@@ -800,6 +792,8 @@ struct qeth_card {
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct list_head vid_list; struct list_head vid_list;
DECLARE_HASHTABLE(mac_htable, 4); DECLARE_HASHTABLE(mac_htable, 4);
DECLARE_HASHTABLE(ip_htable, 4);
DECLARE_HASHTABLE(ip_mc_htable, 4);
struct work_struct kernel_thread_starter; struct work_struct kernel_thread_starter;
spinlock_t thread_mask_lock; spinlock_t thread_mask_lock;
unsigned long thread_start_mask; unsigned long thread_start_mask;
...@@ -807,8 +801,6 @@ struct qeth_card { ...@@ -807,8 +801,6 @@ struct qeth_card {
unsigned long thread_running_mask; unsigned long thread_running_mask;
struct task_struct *recovery_task; struct task_struct *recovery_task;
spinlock_t ip_lock; spinlock_t ip_lock;
struct list_head ip_list;
struct list_head *ip_tbd_list;
struct qeth_ipato ipato; struct qeth_ipato ipato;
struct list_head cmd_waiter_list; struct list_head cmd_waiter_list;
/* QDIO buffer handling */ /* QDIO buffer handling */
......
...@@ -1464,8 +1464,6 @@ static int qeth_setup_card(struct qeth_card *card) ...@@ -1464,8 +1464,6 @@ static int qeth_setup_card(struct qeth_card *card)
card->thread_allowed_mask = 0; card->thread_allowed_mask = 0;
card->thread_running_mask = 0; card->thread_running_mask = 0;
INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); 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_LIST_HEAD(&card->cmd_waiter_list);
init_waitqueue_head(&card->wait_q); init_waitqueue_head(&card->wait_q);
/* initial options */ /* initial options */
...@@ -1500,11 +1498,6 @@ static struct qeth_card *qeth_alloc_card(void) ...@@ -1500,11 +1498,6 @@ static struct qeth_card *qeth_alloc_card(void)
if (!card) if (!card)
goto out; goto out;
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 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)) if (qeth_setup_channel(&card->read))
goto out_ip; goto out_ip;
if (qeth_setup_channel(&card->write)) if (qeth_setup_channel(&card->write))
...@@ -1517,8 +1510,6 @@ static struct qeth_card *qeth_alloc_card(void) ...@@ -1517,8 +1510,6 @@ static struct qeth_card *qeth_alloc_card(void)
out_channel: out_channel:
qeth_clean_channel(&card->read); qeth_clean_channel(&card->read);
out_ip: out_ip:
kfree(card->ip_tbd_list);
out_card:
kfree(card); kfree(card);
out: out:
return NULL; return NULL;
...@@ -4980,7 +4971,6 @@ static void qeth_core_free_card(struct qeth_card *card) ...@@ -4980,7 +4971,6 @@ static void qeth_core_free_card(struct qeth_card *card)
qeth_clean_channel(&card->write); qeth_clean_channel(&card->write);
if (card->dev) if (card->dev)
free_netdev(card->dev); free_netdev(card->dev);
kfree(card->ip_tbd_list);
qeth_free_qdio_buffers(card); qeth_free_qdio_buffers(card);
unregister_service_level(&card->qeth_service_level); unregister_service_level(&card->qeth_service_level);
kfree(card); kfree(card);
......
...@@ -12,4 +12,11 @@ int qeth_l2_create_device_attributes(struct device *); ...@@ -12,4 +12,11 @@ int qeth_l2_create_device_attributes(struct device *);
void qeth_l2_remove_device_attributes(struct device *); void qeth_l2_remove_device_attributes(struct device *);
void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); 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__ */ #endif /* __QETH_L2_H__ */
...@@ -780,7 +780,7 @@ qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) ...@@ -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)) { qeth_l2_mac_hash(ha->addr)) {
if (is_uc == mac->is_uc && if (is_uc == mac->is_uc &&
!memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) { !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; return;
} }
} }
...@@ -792,7 +792,7 @@ qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) ...@@ -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); memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
mac->is_uc = is_uc; 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, hash_add(card->mac_htable, &mac->hnode,
qeth_l2_mac_hash(mac->mac_addr)); qeth_l2_mac_hash(mac->mac_addr));
...@@ -825,7 +825,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) ...@@ -825,7 +825,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
qeth_l2_add_mac(card, ha, 1); qeth_l2_add_mac(card, ha, 1);
hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { 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) if (!mac->is_uc)
rc = qeth_l2_send_delgroupmac(card, rc = qeth_l2_send_delgroupmac(card,
mac->mac_addr); mac->mac_addr);
...@@ -837,15 +837,15 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) ...@@ -837,15 +837,15 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
hash_del(&mac->hnode); hash_del(&mac->hnode);
kfree(mac); 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); rc = qeth_l2_write_mac(card, mac);
if (rc) { if (rc) {
hash_del(&mac->hnode); hash_del(&mac->hnode);
kfree(mac); kfree(mac);
} else } else
mac->disp_flag = QETH_DISP_MAC_DELETE; mac->disp_flag = QETH_DISP_ADDR_DELETE;
} else } else
mac->disp_flag = QETH_DISP_MAC_DELETE; mac->disp_flag = QETH_DISP_ADDR_DELETE;
} }
spin_unlock_bh(&card->mclock); spin_unlock_bh(&card->mclock);
......
...@@ -10,16 +10,23 @@ ...@@ -10,16 +10,23 @@
#define __QETH_L3_H__ #define __QETH_L3_H__
#include "qeth_core.h" #include "qeth_core.h"
#include <linux/hashtable.h>
#define QETH_SNIFF_AVAIL 0x0008 #define QETH_SNIFF_AVAIL 0x0008
struct qeth_ipaddr { struct qeth_ipaddr {
struct list_head entry; struct hlist_node hnode;
enum qeth_ip_types type; enum qeth_ip_types type;
enum qeth_ipa_setdelip_flags set_flags; enum qeth_ipa_setdelip_flags set_flags;
enum qeth_ipa_setdelip_flags del_flags; enum qeth_ipa_setdelip_flags del_flags;
int is_multicast; u8 is_multicast:1;
int users; 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; enum qeth_prot_versions proto;
unsigned char mac[OSA_ADDR_LEN]; unsigned char mac[OSA_ADDR_LEN];
union { union {
...@@ -32,7 +39,24 @@ struct qeth_ipaddr { ...@@ -32,7 +39,24 @@ struct qeth_ipaddr {
unsigned int pfxlen; unsigned int pfxlen;
} a6; } a6;
} u; } 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 qeth_ipato_entry {
struct list_head entry; struct list_head entry;
...@@ -60,6 +84,5 @@ int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *); ...@@ -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); 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_add_ip(struct qeth_card *, struct qeth_ipaddr *);
int qeth_l3_delete_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__ */ #endif /* __QETH_L3_H__ */
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <net/iucv/af_iucv.h> #include <net/iucv/af_iucv.h>
#include <linux/hashtable.h>
#include "qeth_l3.h" #include "qeth_l3.h"
...@@ -57,7 +58,7 @@ static int qeth_l3_isxdigit(char *buf) ...@@ -57,7 +58,7 @@ static int qeth_l3_isxdigit(char *buf)
static void qeth_l3_ipaddr4_to_string(const __u8 *addr, 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) 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, ...@@ -204,104 +205,129 @@ int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
return rc; return rc;
} }
/* inline int
* Add IP to be added to todo list. If there is already an "add todo" qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
* 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; return addr1->proto == addr2->proto &&
int found = 0; !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
!memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac));
}
if (card->options.sniffer) static struct qeth_ipaddr *
return 0; qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) { {
if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) && struct qeth_ipaddr *addr;
(tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
return 0; if (tmp_addr->is_multicast) {
if ((tmp->proto == QETH_PROT_IPV4) && hash_for_each_possible(card->ip_mc_htable, addr,
(addr->proto == QETH_PROT_IPV4) && hnode, qeth_l3_ipaddr_hash(tmp_addr))
(tmp->type == addr->type) && if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
(tmp->is_multicast == addr->is_multicast) && return addr;
(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 { } else {
if (addr->type == QETH_IP_TYPE_DEL_ALL_MC) hash_for_each_possible(card->ip_htable, addr,
list_add(&addr->entry, card->ip_tbd_list); hnode, qeth_l3_ipaddr_hash(tmp_addr))
else { if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
if (addr->users == 0) return addr;
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;
} }
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; int rc = 0;
struct qeth_ipaddr *addr;
QETH_CARD_TEXT(card, 4, "delip"); QETH_CARD_TEXT(card, 4, "delip");
if (addr->proto == QETH_PROT_IPV4) if (tmp_addr->proto == QETH_PROT_IPV4)
QETH_CARD_HEX(card, 4, &addr->u.a4.addr, 4); QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
else { else {
QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8); QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 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); addr = qeth_l3_ip_from_hash(card, tmp_addr);
spin_unlock_irqrestore(&card->ip_lock, flags); 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; 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; int rc = 0;
struct qeth_ipaddr *addr;
QETH_CARD_TEXT(card, 4, "addip"); 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 { else {
QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8); QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 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; return rc;
} }
...@@ -312,229 +338,88 @@ struct qeth_ipaddr *qeth_l3_get_addr_buffer( ...@@ -312,229 +338,88 @@ struct qeth_ipaddr *qeth_l3_get_addr_buffer(
struct qeth_ipaddr *addr; struct qeth_ipaddr *addr;
addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC); addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
if (addr == NULL) { if (!addr)
return NULL; return NULL;
}
addr->type = QETH_IP_TYPE_NORMAL; addr->type = QETH_IP_TYPE_NORMAL;
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
addr->proto = prot; addr->proto = prot;
return addr;
}
static void qeth_l3_delete_mc_addresses(struct qeth_card *card) return addr;
{
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);
} }
/* static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
* 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; struct qeth_ipaddr *addr;
int found = 0; struct hlist_node *tmp;
int i;
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;
QETH_CARD_TEXT(card, 2, "sdiplist"); QETH_CARD_TEXT(card, 4, "clearip");
QETH_CARD_HEX(card, 2, &card, sizeof(void *));
if (!qeth_card_hw_is_reachable(card) || card->options.sniffer) if (recover && card->options.sniffer)
return; return;
spin_lock_irqsave(&card->ip_lock, flags); spin_lock_bh(&card->ip_lock);
tbd_list = card->ip_tbd_list;
card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC); hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
if (!card->ip_tbd_list) { if (!recover) {
QETH_CARD_TEXT(card, 0, "silnomem"); hash_del(&addr->hnode);
card->ip_tbd_list = tbd_list; kfree(addr);
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; continue;
} }
rc = __qeth_l3_ref_ip_on_card(card, todo, &addr); addr->disp_flag = QETH_DISP_ADDR_ADD;
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);
}
} }
spin_unlock_irqrestore(&card->ip_lock, flags);
kfree(tbd_list);
}
static void qeth_l3_clear_ip_list(struct qeth_card *card, int recover) spin_unlock_bh(&card->ip_lock);
{
struct qeth_ipaddr *addr, *tmp;
unsigned long flags;
QETH_CARD_TEXT(card, 4, "clearip"); spin_lock_bh(&card->mclock);
if (recover && card->options.sniffer)
return; hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
spin_lock_irqsave(&card->ip_lock, flags); hash_del(&addr->hnode);
/* clear todo list */
list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry) {
list_del(&addr->entry);
kfree(addr); kfree(addr);
} }
while (!list_empty(&card->ip_list)) { spin_unlock_bh(&card->mclock);
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);
}
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) { QETH_CARD_TEXT(card, 4, "recoverip");
if ((tmp->proto == QETH_PROT_IPV4) &&
(addr->proto == QETH_PROT_IPV4) && spin_lock_bh(&card->ip_lock);
((same_type && (tmp->type == addr->type)) ||
(!same_type && (tmp->type != addr->type))) && hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
(tmp->u.a4.addr == addr->u.a4.addr)) if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
return 1; 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) && spin_unlock_bh(&card->ip_lock);
(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;
}
return 0;
} }
static int qeth_l3_send_setdelmc(struct qeth_card *card, static int qeth_l3_send_setdelmc(struct qeth_card *card,
...@@ -712,27 +597,28 @@ int qeth_l3_setrouting_v6(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) static void qeth_l3_clear_ipato_list(struct qeth_card *card)
{ {
struct qeth_ipato_entry *ipatoe, *tmp; 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_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
list_del(&ipatoe->entry); list_del(&ipatoe->entry);
kfree(ipatoe); 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, int qeth_l3_add_ipato_entry(struct qeth_card *card,
struct qeth_ipato_entry *new) struct qeth_ipato_entry *new)
{ {
struct qeth_ipato_entry *ipatoe; struct qeth_ipato_entry *ipatoe;
unsigned long flags;
int rc = 0; int rc = 0;
QETH_CARD_TEXT(card, 2, "addipato"); 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) { list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
if (ipatoe->proto != new->proto) if (ipatoe->proto != new->proto)
continue; continue;
...@@ -743,10 +629,12 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, ...@@ -743,10 +629,12 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card,
break; break;
} }
} }
if (!rc) if (!rc)
list_add_tail(&new->entry, &card->ipato.entries); list_add_tail(&new->entry, &card->ipato.entries);
spin_unlock_irqrestore(&card->ip_lock, flags); spin_unlock_bh(&card->ip_lock);
return rc; return rc;
} }
...@@ -754,10 +642,11 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card, ...@@ -754,10 +642,11 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card,
enum qeth_prot_versions proto, u8 *addr, int mask_bits) enum qeth_prot_versions proto, u8 *addr, int mask_bits)
{ {
struct qeth_ipato_entry *ipatoe, *tmp; struct qeth_ipato_entry *ipatoe, *tmp;
unsigned long flags;
QETH_CARD_TEXT(card, 2, "delipato"); 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) { list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
if (ipatoe->proto != proto) if (ipatoe->proto != proto)
continue; continue;
...@@ -768,7 +657,8 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card, ...@@ -768,7 +657,8 @@ void qeth_l3_del_ipato_entry(struct qeth_card *card,
kfree(ipatoe); 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, ...@@ -778,7 +668,6 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
const u8 *addr) const u8 *addr)
{ {
struct qeth_ipaddr *ipaddr; struct qeth_ipaddr *ipaddr;
unsigned long flags;
int rc = 0; int rc = 0;
ipaddr = qeth_l3_get_addr_buffer(proto); 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, ...@@ -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; ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG;
} else } else
return -ENOMEM; return -ENOMEM;
spin_lock_irqsave(&card->ip_lock, flags);
if (qeth_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) || spin_lock_bh(&card->ip_lock);
qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
if (!qeth_l3_ip_from_hash(card, ipaddr))
rc = -EEXIST; rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags); else
if (rc) { qeth_l3_add_ip(card, ipaddr);
kfree(ipaddr);
return rc; spin_unlock_bh(&card->ip_lock);
}
if (!qeth_l3_add_ip(card, ipaddr)) kfree(ipaddr);
kfree(ipaddr);
qeth_l3_set_ip_addr_list(card);
return rc; return rc;
} }
...@@ -831,9 +720,12 @@ void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto, ...@@ -831,9 +720,12 @@ void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
ipaddr->type = QETH_IP_TYPE_VIPA; ipaddr->type = QETH_IP_TYPE_VIPA;
} else } else
return; return;
if (!qeth_l3_delete_ip(card, ipaddr))
kfree(ipaddr); spin_lock_bh(&card->ip_lock);
qeth_l3_set_ip_addr_list(card); 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, ...@@ -843,7 +735,6 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
const u8 *addr) const u8 *addr)
{ {
struct qeth_ipaddr *ipaddr; struct qeth_ipaddr *ipaddr;
unsigned long flags;
int rc = 0; int rc = 0;
ipaddr = qeth_l3_get_addr_buffer(proto); 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, ...@@ -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); memcpy(&ipaddr->u.a6.addr, addr, 16);
ipaddr->u.a6.pfxlen = 0; ipaddr->u.a6.pfxlen = 0;
} }
ipaddr->type = QETH_IP_TYPE_RXIP; ipaddr->type = QETH_IP_TYPE_RXIP;
ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG; ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG;
ipaddr->del_flags = 0; ipaddr->del_flags = 0;
} else } else
return -ENOMEM; return -ENOMEM;
spin_lock_irqsave(&card->ip_lock, flags);
if (qeth_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) || spin_lock_bh(&card->ip_lock);
qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
if (!qeth_l3_ip_from_hash(card, ipaddr))
rc = -EEXIST; rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags); else
if (rc) { qeth_l3_add_ip(card, ipaddr);
kfree(ipaddr);
return rc; spin_unlock_bh(&card->ip_lock);
}
if (!qeth_l3_add_ip(card, ipaddr)) kfree(ipaddr);
kfree(ipaddr);
qeth_l3_set_ip_addr_list(card); return rc;
return 0;
} }
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto, 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, ...@@ -896,9 +788,12 @@ void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
ipaddr->type = QETH_IP_TYPE_RXIP; ipaddr->type = QETH_IP_TYPE_RXIP;
} else } else
return; return;
if (!qeth_l3_delete_ip(card, ipaddr))
kfree(ipaddr); spin_lock_bh(&card->ip_lock);
qeth_l3_set_ip_addr_list(card); 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, 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, ...@@ -908,6 +803,7 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card,
int rc = 0; int rc = 0;
int cnt = 3; int cnt = 3;
if (addr->proto == QETH_PROT_IPV4) { if (addr->proto == QETH_PROT_IPV4) {
QETH_CARD_TEXT(card, 2, "setaddr4"); QETH_CARD_TEXT(card, 2, "setaddr4");
QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); 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) ...@@ -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); return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
} }
static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac, static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac)
struct net_device *dev)
{ {
ip_eth_mc_map(ipm, 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 ip_mc_list *im4;
struct qeth_ipaddr *tmp, *ipm;
char buf[MAX_ADDR_LEN]; char buf[MAX_ADDR_LEN];
QETH_CARD_TEXT(card, 4, "addmc"); 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; for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL;
im4 = rcu_dereference(im4->next_rcu)) { im4 = rcu_dereference(im4->next_rcu)) {
qeth_l3_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev); qeth_l3_get_mac_for_ipm(im4->multiaddr, buf);
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
if (!ipm) tmp->u.a4.addr = im4->multiaddr;
continue; memcpy(tmp->mac, buf, sizeof(tmp->mac));
ipm->u.a4.addr = im4->multiaddr;
memcpy(ipm->mac, buf, OSA_ADDR_LEN); ipm = qeth_l3_ip_from_hash(card, tmp);
ipm->is_multicast = 1; if (ipm) {
if (!qeth_l3_add_ip(card, ipm)) ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
kfree(ipm); } 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 */ /* called with rcu_read_lock */
...@@ -1541,6 +1505,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) ...@@ -1541,6 +1505,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)
u16 vid; u16 vid;
QETH_CARD_TEXT(card, 4, "addmcvl"); QETH_CARD_TEXT(card, 4, "addmcvl");
if (!qeth_is_supported(card, IPA_FULL_VLAN)) if (!qeth_is_supported(card, IPA_FULL_VLAN))
return; return;
...@@ -1555,7 +1520,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) ...@@ -1555,7 +1520,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)
in_dev = __in_dev_get_rcu(netdev); in_dev = __in_dev_get_rcu(netdev);
if (!in_dev) if (!in_dev)
continue; continue;
qeth_l3_add_mc(card, in_dev); qeth_l3_add_mc_to_hash(card, in_dev);
} }
} }
...@@ -1564,36 +1529,60 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) ...@@ -1564,36 +1529,60 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card)
struct in_device *in4_dev; struct in_device *in4_dev;
QETH_CARD_TEXT(card, 4, "chkmcv4"); QETH_CARD_TEXT(card, 4, "chkmcv4");
rcu_read_lock(); rcu_read_lock();
in4_dev = __in_dev_get_rcu(card->dev); in4_dev = __in_dev_get_rcu(card->dev);
if (in4_dev == NULL) if (in4_dev == NULL)
goto unlock; goto unlock;
qeth_l3_add_mc(card, in4_dev); qeth_l3_add_mc_to_hash(card, in4_dev);
qeth_l3_add_vlan_mc(card); qeth_l3_add_vlan_mc(card);
unlock: unlock:
rcu_read_unlock(); rcu_read_unlock();
} }
#ifdef CONFIG_QETH_IPV6 #ifdef CONFIG_QETH_IPV6
static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) static void
qeth_l3_add_mc6_to_hash(struct qeth_card *card, struct inet6_dev *in6_dev)
{ {
struct qeth_ipaddr *ipm; struct qeth_ipaddr *ipm;
struct ifmcaddr6 *im6; struct ifmcaddr6 *im6;
struct qeth_ipaddr *tmp;
char buf[MAX_ADDR_LEN]; char buf[MAX_ADDR_LEN];
QETH_CARD_TEXT(card, 4, "addmc6"); QETH_CARD_TEXT(card, 4, "addmc6");
tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (!tmp)
return;
for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) {
ndisc_mc_map(&im6->mca_addr, buf, in6_dev->dev, 0); ndisc_mc_map(&im6->mca_addr, buf, in6_dev->dev, 0);
memcpy(tmp->mac, buf, sizeof(tmp->mac));
memcpy(&tmp->u.a6.addr, &im6->mca_addr.s6_addr,
sizeof(struct in6_addr));
tmp->is_multicast = 1;
ipm = qeth_l3_ip_from_hash(card, tmp);
if (ipm) {
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
continue;
}
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (!ipm) if (!ipm)
continue; continue;
ipm->is_multicast = 1;
memcpy(ipm->mac, buf, OSA_ADDR_LEN); memcpy(ipm->mac, buf, OSA_ADDR_LEN);
memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr, memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr,
sizeof(struct in6_addr)); sizeof(struct in6_addr));
if (!qeth_l3_add_ip(card, ipm)) ipm->is_multicast = 1;
kfree(ipm); 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 */ /* called with rcu_read_lock */
...@@ -1603,6 +1592,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) ...@@ -1603,6 +1592,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
u16 vid; u16 vid;
QETH_CARD_TEXT(card, 4, "admc6vl"); QETH_CARD_TEXT(card, 4, "admc6vl");
if (!qeth_is_supported(card, IPA_FULL_VLAN)) if (!qeth_is_supported(card, IPA_FULL_VLAN))
return; return;
...@@ -1618,7 +1608,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) ...@@ -1618,7 +1608,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
if (!in_dev) if (!in_dev)
continue; continue;
read_lock_bh(&in_dev->lock); read_lock_bh(&in_dev->lock);
qeth_l3_add_mc6(card, in_dev); qeth_l3_add_mc6_to_hash(card, in_dev);
read_unlock_bh(&in_dev->lock); read_unlock_bh(&in_dev->lock);
in6_dev_put(in_dev); in6_dev_put(in_dev);
} }
...@@ -1629,14 +1619,16 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) ...@@ -1629,14 +1619,16 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card)
struct inet6_dev *in6_dev; struct inet6_dev *in6_dev;
QETH_CARD_TEXT(card, 4, "chkmcv6"); QETH_CARD_TEXT(card, 4, "chkmcv6");
if (!qeth_is_supported(card, IPA_IPV6)) if (!qeth_is_supported(card, IPA_IPV6))
return ; return ;
in6_dev = in6_dev_get(card->dev); in6_dev = in6_dev_get(card->dev);
if (in6_dev == NULL) if (!in6_dev)
return; return;
rcu_read_lock(); rcu_read_lock();
read_lock_bh(&in6_dev->lock); read_lock_bh(&in6_dev->lock);
qeth_l3_add_mc6(card, in6_dev); qeth_l3_add_mc6_to_hash(card, in6_dev);
qeth_l3_add_vlan_mc6(card); qeth_l3_add_vlan_mc6(card);
read_unlock_bh(&in6_dev->lock); read_unlock_bh(&in6_dev->lock);
rcu_read_unlock(); rcu_read_unlock();
...@@ -1660,16 +1652,23 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card, ...@@ -1660,16 +1652,23 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
in_dev = in_dev_get(netdev); in_dev = in_dev_get(netdev);
if (!in_dev) if (!in_dev)
return; return;
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
if (!addr)
return;
spin_lock_bh(&card->ip_lock);
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); addr->u.a4.addr = ifa->ifa_address;
if (addr) { addr->u.a4.mask = ifa->ifa_mask;
addr->u.a4.addr = ifa->ifa_address; addr->type = QETH_IP_TYPE_NORMAL;
addr->u.a4.mask = ifa->ifa_mask; qeth_l3_delete_ip(card, addr);
addr->type = QETH_IP_TYPE_NORMAL;
if (!qeth_l3_delete_ip(card, addr))
kfree(addr);
}
} }
spin_unlock_bh(&card->ip_lock);
kfree(addr);
in_dev_put(in_dev); in_dev_put(in_dev);
} }
...@@ -1687,20 +1686,28 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, ...@@ -1687,20 +1686,28 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
if (!netdev) if (!netdev)
return; return;
in6_dev = in6_dev_get(netdev); in6_dev = in6_dev_get(netdev);
if (!in6_dev) if (!in6_dev)
return; return;
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (!addr)
return;
spin_lock_bh(&card->ip_lock);
list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); memcpy(&addr->u.a6.addr, &ifa->addr,
if (addr) { sizeof(struct in6_addr));
memcpy(&addr->u.a6.addr, &ifa->addr, addr->u.a6.pfxlen = ifa->prefix_len;
sizeof(struct in6_addr)); addr->type = QETH_IP_TYPE_NORMAL;
addr->u.a6.pfxlen = ifa->prefix_len; qeth_l3_delete_ip(card, addr);
addr->type = QETH_IP_TYPE_NORMAL;
if (!qeth_l3_delete_ip(card, addr))
kfree(addr);
}
} }
spin_unlock_bh(&card->ip_lock);
kfree(addr);
in6_dev_put(in6_dev); in6_dev_put(in6_dev);
#endif /* CONFIG_QETH_IPV6 */ #endif /* CONFIG_QETH_IPV6 */
} }
...@@ -1727,18 +1734,16 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, ...@@ -1727,18 +1734,16 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev,
__be16 proto, u16 vid) __be16 proto, u16 vid)
{ {
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
unsigned long flags;
QETH_CARD_TEXT_(card, 4, "kid:%d", vid); QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
QETH_CARD_TEXT(card, 3, "kidREC"); QETH_CARD_TEXT(card, 3, "kidREC");
return 0; return 0;
} }
spin_lock_irqsave(&card->vlanlock, flags);
/* unregister IP addresses of vlan device */ /* unregister IP addresses of vlan device */
qeth_l3_free_vlan_addresses(card, vid); qeth_l3_free_vlan_addresses(card, vid);
clear_bit(vid, card->active_vlans); clear_bit(vid, card->active_vlans);
spin_unlock_irqrestore(&card->vlanlock, flags);
qeth_l3_set_multicast_list(card->dev); qeth_l3_set_multicast_list(card->dev);
return 0; return 0;
} }
...@@ -1994,8 +1999,8 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, ...@@ -1994,8 +1999,8 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
static int qeth_l3_verify_dev(struct net_device *dev) static int qeth_l3_verify_dev(struct net_device *dev)
{ {
struct qeth_card *card; struct qeth_card *card;
unsigned long flags;
int rc = 0; int rc = 0;
unsigned long flags;
read_lock_irqsave(&qeth_core_card_list.rwlock, flags); read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
list_for_each_entry(card, &qeth_core_card_list.list, list) { list_for_each_entry(card, &qeth_core_card_list.list, list) {
...@@ -2051,7 +2056,7 @@ static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) ...@@ -2051,7 +2056,7 @@ static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
card->state = CARD_STATE_SOFTSETUP; card->state = CARD_STATE_SOFTSETUP;
} }
if (card->state == CARD_STATE_SOFTSETUP) { if (card->state == CARD_STATE_SOFTSETUP) {
qeth_l3_clear_ip_list(card, 1); qeth_l3_clear_ip_htable(card, 1);
qeth_clear_ipacmd_list(card); qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP; card->state = CARD_STATE_HARDSETUP;
} }
...@@ -2106,12 +2111,20 @@ static void qeth_l3_set_multicast_list(struct net_device *dev) ...@@ -2106,12 +2111,20 @@ static void qeth_l3_set_multicast_list(struct net_device *dev)
(card->state != CARD_STATE_UP)) (card->state != CARD_STATE_UP))
return; return;
if (!card->options.sniffer) { if (!card->options.sniffer) {
qeth_l3_delete_mc_addresses(card);
spin_lock_bh(&card->mclock);
qeth_l3_mark_all_mc_to_be_deleted(card);
qeth_l3_add_multicast_ipv4(card); qeth_l3_add_multicast_ipv4(card);
#ifdef CONFIG_QETH_IPV6 #ifdef CONFIG_QETH_IPV6
qeth_l3_add_multicast_ipv6(card); qeth_l3_add_multicast_ipv6(card);
#endif #endif
qeth_l3_set_ip_addr_list(card); qeth_l3_delete_nonused_mc(card);
qeth_l3_add_all_new_mc(card);
spin_unlock_bh(&card->mclock);
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
return; return;
} }
...@@ -3261,7 +3274,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) ...@@ -3261,7 +3274,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
card->dev = NULL; card->dev = NULL;
} }
qeth_l3_clear_ip_list(card, 0); qeth_l3_clear_ip_htable(card, 0);
qeth_l3_clear_ipato_list(card); qeth_l3_clear_ipato_list(card);
return; return;
} }
...@@ -3346,7 +3359,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) ...@@ -3346,7 +3359,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
card->state = CARD_STATE_SOFTSETUP; card->state = CARD_STATE_SOFTSETUP;
qeth_set_allowed_threads(card, 0xffffffff, 0); qeth_set_allowed_threads(card, 0xffffffff, 0);
qeth_l3_set_ip_addr_list(card); qeth_l3_recover_ip(card);
if (card->lan_online) if (card->lan_online)
netif_carrier_on(card->dev); netif_carrier_on(card->dev);
else else
...@@ -3547,6 +3560,7 @@ EXPORT_SYMBOL_GPL(qeth_l3_discipline); ...@@ -3547,6 +3560,7 @@ EXPORT_SYMBOL_GPL(qeth_l3_discipline);
static int qeth_l3_ip_event(struct notifier_block *this, static int qeth_l3_ip_event(struct notifier_block *this,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct net_device *dev = (struct net_device *)ifa->ifa_dev->dev; struct net_device *dev = (struct net_device *)ifa->ifa_dev->dev;
struct qeth_ipaddr *addr; struct qeth_ipaddr *addr;
...@@ -3561,27 +3575,27 @@ static int qeth_l3_ip_event(struct notifier_block *this, ...@@ -3561,27 +3575,27 @@ static int qeth_l3_ip_event(struct notifier_block *this,
QETH_CARD_TEXT(card, 3, "ipevent"); QETH_CARD_TEXT(card, 3, "ipevent");
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
if (addr != NULL) { if (addr) {
addr->u.a4.addr = ifa->ifa_address; addr->u.a4.addr = ifa->ifa_address;
addr->u.a4.mask = ifa->ifa_mask; addr->u.a4.mask = ifa->ifa_mask;
addr->type = QETH_IP_TYPE_NORMAL; addr->type = QETH_IP_TYPE_NORMAL;
} else } else
goto out; return NOTIFY_DONE;
switch (event) { switch (event) {
case NETDEV_UP: case NETDEV_UP:
if (!qeth_l3_add_ip(card, addr)) spin_lock_bh(&card->ip_lock);
kfree(addr); qeth_l3_add_ip(card, addr);
spin_unlock_bh(&card->ip_lock);
break; break;
case NETDEV_DOWN: case NETDEV_DOWN:
if (!qeth_l3_delete_ip(card, addr)) spin_lock_bh(&card->ip_lock);
kfree(addr); qeth_l3_delete_ip(card, addr);
break; spin_unlock_bh(&card->ip_lock);
default:
break; break;
} }
qeth_l3_set_ip_addr_list(card);
out: kfree(addr);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -3610,27 +3624,27 @@ static int qeth_l3_ip6_event(struct notifier_block *this, ...@@ -3610,27 +3624,27 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
return NOTIFY_DONE; return NOTIFY_DONE;
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (addr != NULL) { if (addr) {
memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr)); memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr));
addr->u.a6.pfxlen = ifa->prefix_len; addr->u.a6.pfxlen = ifa->prefix_len;
addr->type = QETH_IP_TYPE_NORMAL; addr->type = QETH_IP_TYPE_NORMAL;
} else } else
goto out; return NOTIFY_DONE;
switch (event) { switch (event) {
case NETDEV_UP: case NETDEV_UP:
if (!qeth_l3_add_ip(card, addr)) spin_lock_bh(&card->ip_lock);
kfree(addr); qeth_l3_add_ip(card, addr);
spin_unlock_bh(&card->ip_lock);
break; break;
case NETDEV_DOWN: case NETDEV_DOWN:
if (!qeth_l3_delete_ip(card, addr)) spin_lock_bh(&card->ip_lock);
kfree(addr); qeth_l3_delete_ip(card, addr);
break; spin_unlock_bh(&card->ip_lock);
default:
break; break;
} }
qeth_l3_set_ip_addr_list(card);
out: kfree(addr);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <linux/hashtable.h>
#include "qeth_l3.h" #include "qeth_l3.h"
#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
...@@ -285,19 +286,19 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, ...@@ -285,19 +286,19 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
if (card->options.hsuid[0]) { if (card->options.hsuid[0]) {
/* delete old ip address */ /* delete old ip address */
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (addr != NULL) { if (!addr)
addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] =
card->options.hsuid[i - 8];
addr->u.a6.pfxlen = 0;
addr->type = QETH_IP_TYPE_NORMAL;
} else
return -ENOMEM; return -ENOMEM;
if (!qeth_l3_delete_ip(card, addr))
kfree(addr); addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
qeth_l3_set_ip_addr_list(card); addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] =
card->options.hsuid[i - 8];
addr->u.a6.pfxlen = 0;
addr->type = QETH_IP_TYPE_NORMAL;
qeth_l3_delete_ip(card, addr);
kfree(addr);
} }
if (strlen(tmp) == 0) { if (strlen(tmp) == 0) {
...@@ -328,9 +329,8 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, ...@@ -328,9 +329,8 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
addr->type = QETH_IP_TYPE_NORMAL; addr->type = QETH_IP_TYPE_NORMAL;
} else } else
return -ENOMEM; return -ENOMEM;
if (!qeth_l3_add_ip(card, addr)) qeth_l3_add_ip(card, addr);
kfree(addr); kfree(addr);
qeth_l3_set_ip_addr_list(card);
return count; return count;
} }
...@@ -367,8 +367,8 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, ...@@ -367,8 +367,8 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count) struct device_attribute *attr, const char *buf, size_t count)
{ {
struct qeth_card *card = dev_get_drvdata(dev); struct qeth_card *card = dev_get_drvdata(dev);
struct qeth_ipaddr *tmpipa, *t; struct qeth_ipaddr *addr;
int rc = 0; int i, rc = 0;
if (!card) if (!card)
return -EINVAL; return -EINVAL;
...@@ -384,21 +384,20 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, ...@@ -384,21 +384,20 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
card->ipato.enabled = (card->ipato.enabled)? 0 : 1; card->ipato.enabled = (card->ipato.enabled)? 0 : 1;
} else if (sysfs_streq(buf, "1")) { } else if (sysfs_streq(buf, "1")) {
card->ipato.enabled = 1; card->ipato.enabled = 1;
list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) { hash_for_each(card->ip_htable, i, addr, hnode) {
if ((tmpipa->type == QETH_IP_TYPE_NORMAL) && if ((addr->type == QETH_IP_TYPE_NORMAL) &&
qeth_l3_is_addr_covered_by_ipato(card, tmpipa)) qeth_l3_is_addr_covered_by_ipato(card, addr))
tmpipa->set_flags |= addr->set_flags |=
QETH_IPA_SETIP_TAKEOVER_FLAG; QETH_IPA_SETIP_TAKEOVER_FLAG;
} }
} else if (sysfs_streq(buf, "0")) { } else if (sysfs_streq(buf, "0")) {
card->ipato.enabled = 0; card->ipato.enabled = 0;
list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) { hash_for_each(card->ip_htable, i, addr, hnode) {
if (tmpipa->set_flags & if (addr->set_flags &
QETH_IPA_SETIP_TAKEOVER_FLAG) QETH_IPA_SETIP_TAKEOVER_FLAG)
tmpipa->set_flags &= addr->set_flags &=
~QETH_IPA_SETIP_TAKEOVER_FLAG; ~QETH_IPA_SETIP_TAKEOVER_FLAG;
} }
} else } else
rc = -EINVAL; rc = -EINVAL;
out: out:
...@@ -452,7 +451,6 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, ...@@ -452,7 +451,6 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
enum qeth_prot_versions proto) enum qeth_prot_versions proto)
{ {
struct qeth_ipato_entry *ipatoe; struct qeth_ipato_entry *ipatoe;
unsigned long flags;
char addr_str[40]; char addr_str[40];
int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
int i = 0; int i = 0;
...@@ -460,7 +458,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, ...@@ -460,7 +458,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
/* add strlen for "/<mask>\n" */ /* add strlen for "/<mask>\n" */
entry_len += (proto == QETH_PROT_IPV4)? 5 : 6; entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
spin_lock_irqsave(&card->ip_lock, flags); spin_lock_bh(&card->ip_lock);
list_for_each_entry(ipatoe, &card->ipato.entries, entry) { list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
if (ipatoe->proto != proto) if (ipatoe->proto != proto)
continue; continue;
...@@ -473,7 +471,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, ...@@ -473,7 +471,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
i += snprintf(buf + i, PAGE_SIZE - i, i += snprintf(buf + i, PAGE_SIZE - i,
"%s/%i\n", addr_str, ipatoe->mask_bits); "%s/%i\n", addr_str, ipatoe->mask_bits);
} }
spin_unlock_irqrestore(&card->ip_lock, flags); spin_unlock_bh(&card->ip_lock);
i += snprintf(buf + i, PAGE_SIZE - i, "\n"); i += snprintf(buf + i, PAGE_SIZE - i, "\n");
return i; return i;
...@@ -689,15 +687,15 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card, ...@@ -689,15 +687,15 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card,
enum qeth_prot_versions proto) enum qeth_prot_versions proto)
{ {
struct qeth_ipaddr *ipaddr; struct qeth_ipaddr *ipaddr;
struct hlist_node *tmp;
char addr_str[40]; char addr_str[40];
int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
unsigned long flags;
int i = 0; int i = 0;
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
entry_len += 2; /* \n + terminator */ entry_len += 2; /* \n + terminator */
spin_lock_irqsave(&card->ip_lock, flags); spin_lock_bh(&card->ip_lock);
list_for_each_entry(ipaddr, &card->ip_list, entry) { hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) {
if (ipaddr->proto != proto) if (ipaddr->proto != proto)
continue; continue;
if (ipaddr->type != QETH_IP_TYPE_VIPA) if (ipaddr->type != QETH_IP_TYPE_VIPA)
...@@ -711,7 +709,7 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card, ...@@ -711,7 +709,7 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card,
addr_str); addr_str);
i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
} }
spin_unlock_irqrestore(&card->ip_lock, flags); spin_unlock_bh(&card->ip_lock);
i += snprintf(buf + i, PAGE_SIZE - i, "\n"); i += snprintf(buf + i, PAGE_SIZE - i, "\n");
return i; return i;
...@@ -851,15 +849,15 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card, ...@@ -851,15 +849,15 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card,
enum qeth_prot_versions proto) enum qeth_prot_versions proto)
{ {
struct qeth_ipaddr *ipaddr; struct qeth_ipaddr *ipaddr;
struct hlist_node *tmp;
char addr_str[40]; char addr_str[40];
int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
unsigned long flags;
int i = 0; int i = 0;
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
entry_len += 2; /* \n + terminator */ entry_len += 2; /* \n + terminator */
spin_lock_irqsave(&card->ip_lock, flags); spin_lock_bh(&card->ip_lock);
list_for_each_entry(ipaddr, &card->ip_list, entry) { hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) {
if (ipaddr->proto != proto) if (ipaddr->proto != proto)
continue; continue;
if (ipaddr->type != QETH_IP_TYPE_RXIP) if (ipaddr->type != QETH_IP_TYPE_RXIP)
...@@ -873,7 +871,7 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card, ...@@ -873,7 +871,7 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card,
addr_str); addr_str);
i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
} }
spin_unlock_irqrestore(&card->ip_lock, flags); spin_unlock_bh(&card->ip_lock);
i += snprintf(buf + i, PAGE_SIZE - i, "\n"); i += snprintf(buf + i, PAGE_SIZE - i, "\n");
return i; return i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment