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

s390/qeth: optimize MAC handling in rx_mode callback

In layer2 mode of the qeth driver, MAC address lists
from struct net_device require mapping to the OSA-card.
The existing implementation is inefficient for lists with
more than several MAC addresses, since for every
ndo_set_rx_mode callback it removes all MAC addresses first,
and then registers the current MAC address list.
This patch changes implementation of ndo_set_rx_mode callback
in qeth, only performing hardware registration/removal for
new/deleted addresses. To shorten lookup of MAC addresses
registered addresses are kept in a hashtable instead of a
linear list.
Signed-off-by: default avatarLakhvich Dmitriy <ldmitriy@ru.ibm.com>
Signed-off-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Reviewed-by: default avatarEugene Crosser <Eugene.Crosser@ru.ibm.com>
Reviewed-by: default avatarThomas Richter <tmricht@de.ibm.com>
Tested-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9abfa8cb
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/hashtable.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/if_inet6.h> #include <net/if_inet6.h>
...@@ -739,11 +740,17 @@ struct qeth_vlan_vid { ...@@ -739,11 +740,17 @@ struct qeth_vlan_vid {
unsigned short vid; unsigned short vid;
}; };
struct qeth_mc_mac { enum qeth_mac_disposition {
struct list_head list; QETH_DISP_MAC_DELETE = 0,
__u8 mc_addr[MAX_ADDR_LEN]; QETH_DISP_MAC_DO_NOTHING = 1,
unsigned char mc_addrlen; QETH_DISP_MAC_ADD = 2,
int is_vmac; };
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 {
...@@ -790,7 +797,7 @@ struct qeth_card { ...@@ -790,7 +797,7 @@ struct qeth_card {
spinlock_t mclock; spinlock_t mclock;
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;
struct list_head mc_list; DECLARE_HASHTABLE(mac_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;
......
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/hash.h>
#include <linux/hashtable.h>
#include <linux/string.h>
#include "qeth_core.h" #include "qeth_core.h"
#include "qeth_l2.h" #include "qeth_l2.h"
...@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *); ...@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *);
static int qeth_l2_send_delmac(struct qeth_card *, __u8 *); static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *, static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
enum qeth_ipa_cmds); enum qeth_ipa_cmds);
static void qeth_l2_set_multicast_list(struct net_device *); static void qeth_l2_set_rx_mode(struct net_device *);
static int qeth_l2_recover(void *); static int qeth_l2_recover(void *);
static void qeth_bridgeport_query_support(struct qeth_card *card); static void qeth_bridgeport_query_support(struct qeth_card *card);
static void qeth_bridge_state_change(struct qeth_card *card, static void qeth_bridge_state_change(struct qeth_card *card,
...@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) ...@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
return rc; return rc;
} }
static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac) static inline u32 qeth_l2_mac_hash(const u8 *addr)
{ {
struct qeth_mc_mac *mc; return get_unaligned((u32 *)(&addr[2]));
int rc; }
mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
if (!mc) static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
return; {
memcpy(mc->mc_addr, mac, OSA_ADDR_LEN); int rc;
mc->mc_addrlen = OSA_ADDR_LEN;
mc->is_vmac = vmac;
if (vmac) { if (mac->is_uc) {
rc = qeth_setdel_makerc(card, rc = qeth_setdel_makerc(card,
qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC)); qeth_l2_send_setdelmac(card, mac->mac_addr,
IPA_CMD_SETVMAC));
} else { } else {
rc = qeth_setdel_makerc(card, rc = qeth_setdel_makerc(card,
qeth_l2_send_setgroupmac(card, mac)); qeth_l2_send_setgroupmac(card, mac->mac_addr));
} }
return rc;
if (!rc)
list_add_tail(&mc->list, &card->mc_list);
else
kfree(mc);
} }
static void qeth_l2_del_all_mc(struct qeth_card *card, int del) static void qeth_l2_del_all_macs(struct qeth_card *card, int del)
{ {
struct qeth_mc_mac *mc, *tmp; struct qeth_mac *mac;
struct hlist_node *tmp;
int i;
spin_lock_bh(&card->mclock); spin_lock_bh(&card->mclock);
list_for_each_entry_safe(mc, tmp, &card->mc_list, list) { hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
if (del) { if (del) {
if (mc->is_vmac) if (mac->is_uc)
qeth_l2_send_setdelmac(card, mc->mc_addr, qeth_l2_send_setdelmac(card, mac->mac_addr,
IPA_CMD_DELVMAC); IPA_CMD_DELVMAC);
else else
qeth_l2_send_delgroupmac(card, mc->mc_addr); qeth_l2_send_delgroupmac(card, mac->mac_addr);
} }
list_del(&mc->list); hash_del(&mac->hnode);
kfree(mc); kfree(mac);
} }
spin_unlock_bh(&card->mclock); spin_unlock_bh(&card->mclock);
} }
...@@ -403,7 +400,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, ...@@ -403,7 +400,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
kfree(tmpid); kfree(tmpid);
} }
qeth_l2_set_multicast_list(card->dev); qeth_l2_set_rx_mode(card->dev);
return rc; return rc;
} }
...@@ -460,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) ...@@ -460,7 +457,7 @@ static void qeth_l2_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_l2_del_all_mc(card, 0); qeth_l2_del_all_macs(card, 0);
qeth_clear_ipacmd_list(card); qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP; card->state = CARD_STATE_HARDSETUP;
} }
...@@ -768,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card) ...@@ -768,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card)
card->options.sbp.role = role; card->options.sbp.role = role;
card->info.promisc_mode = promisc_mode; card->info.promisc_mode = promisc_mode;
} }
}
/* New MAC address is added to the hash table and marked to be written on card
* only if there is not in the hash table storage already
*
*/
static void
qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc)
{
struct qeth_mac *mac;
hash_for_each_possible(card->mac_htable, mac, hnode,
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;
return;
}
}
mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
if (!mac)
return;
memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
mac->is_uc = is_uc;
mac->disp_flag = QETH_DISP_MAC_ADD;
hash_add(card->mac_htable, &mac->hnode,
qeth_l2_mac_hash(mac->mac_addr));
} }
static void qeth_l2_set_multicast_list(struct net_device *dev) static void qeth_l2_set_rx_mode(struct net_device *dev)
{ {
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
struct netdev_hw_addr *ha; struct netdev_hw_addr *ha;
struct qeth_mac *mac;
struct hlist_node *tmp;
int i;
int rc;
if (card->info.type == QETH_CARD_TYPE_OSN) if (card->info.type == QETH_CARD_TYPE_OSN)
return ; return;
QETH_CARD_TEXT(card, 3, "setmulti"); QETH_CARD_TEXT(card, 3, "setmulti");
if (qeth_threads_running(card, QETH_RECOVER_THREAD) && if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
(card->state != CARD_STATE_UP)) (card->state != CARD_STATE_UP))
return; return;
qeth_l2_del_all_mc(card, 1);
spin_lock_bh(&card->mclock); spin_lock_bh(&card->mclock);
netdev_for_each_mc_addr(ha, dev) netdev_for_each_mc_addr(ha, dev)
qeth_l2_add_mc(card, ha->addr, 0); qeth_l2_add_mac(card, ha, 0);
netdev_for_each_uc_addr(ha, dev) netdev_for_each_uc_addr(ha, dev)
qeth_l2_add_mc(card, ha->addr, 1); 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->is_uc)
rc = qeth_l2_send_delgroupmac(card,
mac->mac_addr);
else {
rc = qeth_l2_send_setdelmac(card, mac->mac_addr,
IPA_CMD_DELVMAC);
}
hash_del(&mac->hnode);
kfree(mac);
} else if (mac->disp_flag == QETH_DISP_MAC_ADD) {
rc = qeth_l2_write_mac(card, mac);
if (rc) {
hash_del(&mac->hnode);
kfree(mac);
} else
mac->disp_flag = QETH_DISP_MAC_DELETE;
} else
mac->disp_flag = QETH_DISP_MAC_DELETE;
}
spin_unlock_bh(&card->mclock); 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))
qeth_setadp_promisc_mode(card); qeth_setadp_promisc_mode(card);
else else
...@@ -974,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) ...@@ -974,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
qeth_l2_create_device_attributes(&gdev->dev); qeth_l2_create_device_attributes(&gdev->dev);
INIT_LIST_HEAD(&card->vid_list); INIT_LIST_HEAD(&card->vid_list);
INIT_LIST_HEAD(&card->mc_list); hash_init(card->mac_htable);
card->options.layer2 = 1; card->options.layer2 = 1;
card->info.hwtrap = 0; card->info.hwtrap = 0;
return 0; return 0;
...@@ -1020,7 +1079,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = { ...@@ -1020,7 +1079,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_get_stats = qeth_get_stats, .ndo_get_stats = qeth_get_stats,
.ndo_start_xmit = qeth_l2_hard_start_xmit, .ndo_start_xmit = qeth_l2_hard_start_xmit,
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = qeth_l2_set_multicast_list, .ndo_set_rx_mode = qeth_l2_set_rx_mode,
.ndo_do_ioctl = qeth_l2_do_ioctl, .ndo_do_ioctl = qeth_l2_do_ioctl,
.ndo_set_mac_address = qeth_l2_set_mac_address, .ndo_set_mac_address = qeth_l2_set_mac_address,
.ndo_change_mtu = qeth_change_mtu, .ndo_change_mtu = qeth_change_mtu,
...@@ -1179,7 +1238,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) ...@@ -1179,7 +1238,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
rtnl_unlock(); rtnl_unlock();
} }
/* this also sets saved unicast addresses */ /* this also sets saved unicast addresses */
qeth_l2_set_multicast_list(card->dev); qeth_l2_set_rx_mode(card->dev);
} }
/* let user_space know that device is online */ /* let user_space know that device is online */
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
......
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