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 @@
#include <linux/bitops.h>
#include <linux/seq_file.h>
#include <linux/ethtool.h>
#include <linux/hashtable.h>
#include <net/ipv6.h>
#include <net/if_inet6.h>
......@@ -739,11 +740,17 @@ struct qeth_vlan_vid {
unsigned short vid;
};
struct qeth_mc_mac {
struct list_head list;
__u8 mc_addr[MAX_ADDR_LEN];
unsigned char mc_addrlen;
int is_vmac;
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;
};
struct qeth_rx {
......@@ -790,7 +797,7 @@ struct qeth_card {
spinlock_t mclock;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct list_head vid_list;
struct list_head mc_list;
DECLARE_HASHTABLE(mac_htable, 4);
struct work_struct kernel_thread_starter;
spinlock_t thread_mask_lock;
unsigned long thread_start_mask;
......
......@@ -19,7 +19,9 @@
#include <linux/mii.h>
#include <linux/ip.h>
#include <linux/list.h>
#include <linux/hash.h>
#include <linux/hashtable.h>
#include <linux/string.h>
#include "qeth_core.h"
#include "qeth_l2.h"
......@@ -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_setdelmac(struct qeth_card *, __u8 *,
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 void qeth_bridgeport_query_support(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)
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;
int rc;
mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
return get_unaligned((u32 *)(&addr[2]));
}
if (!mc)
return;
static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
{
memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
mc->mc_addrlen = OSA_ADDR_LEN;
mc->is_vmac = vmac;
int rc;
if (vmac) {
if (mac->is_uc) {
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 {
rc = qeth_setdel_makerc(card,
qeth_l2_send_setgroupmac(card, mac));
qeth_l2_send_setgroupmac(card, mac->mac_addr));
}
if (!rc)
list_add_tail(&mc->list, &card->mc_list);
else
kfree(mc);
return rc;
}
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);
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 (mc->is_vmac)
qeth_l2_send_setdelmac(card, mc->mc_addr,
IPA_CMD_DELVMAC);
if (mac->is_uc)
qeth_l2_send_setdelmac(card, mac->mac_addr,
IPA_CMD_DELVMAC);
else
qeth_l2_send_delgroupmac(card, mc->mc_addr);
qeth_l2_send_delgroupmac(card, mac->mac_addr);
}
list_del(&mc->list);
kfree(mc);
hash_del(&mac->hnode);
kfree(mac);
}
spin_unlock_bh(&card->mclock);
}
......@@ -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);
kfree(tmpid);
}
qeth_l2_set_multicast_list(card->dev);
qeth_l2_set_rx_mode(card->dev);
return rc;
}
......@@ -460,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
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);
card->state = CARD_STATE_HARDSETUP;
}
......@@ -768,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card)
card->options.sbp.role = role;
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 netdev_hw_addr *ha;
struct qeth_mac *mac;
struct hlist_node *tmp;
int i;
int rc;
if (card->info.type == QETH_CARD_TYPE_OSN)
return ;
return;
QETH_CARD_TEXT(card, 3, "setmulti");
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
(card->state != CARD_STATE_UP))
return;
qeth_l2_del_all_mc(card, 1);
spin_lock_bh(&card->mclock);
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)
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);
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
qeth_setadp_promisc_mode(card);
else
......@@ -974,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
qeth_l2_create_device_attributes(&gdev->dev);
INIT_LIST_HEAD(&card->vid_list);
INIT_LIST_HEAD(&card->mc_list);
hash_init(card->mac_htable);
card->options.layer2 = 1;
card->info.hwtrap = 0;
return 0;
......@@ -1020,7 +1079,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_get_stats = qeth_get_stats,
.ndo_start_xmit = qeth_l2_hard_start_xmit,
.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_set_mac_address = qeth_l2_set_mac_address,
.ndo_change_mtu = qeth_change_mtu,
......@@ -1179,7 +1238,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
rtnl_unlock();
}
/* 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 */
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