i40iw: Add IP addr handling on netdev events

Disable listeners and disconnect all connected QPs on
a netdev interface down event. On an interface up event,
the listeners are re-enabled.

Signed-off-by: Mustafa Ismail <mustafa.ismail@intel.com>
Signed-off-by: Shiraz Saleem <shiraz.saleem@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
Mustafa Ismail 2016-11-30 15:07:30 -06:00 committed by Doug Ledford
parent d59659340c
commit e5e74b61b1
3 changed files with 156 additions and 42 deletions

View File

@ -1556,9 +1556,15 @@ static enum i40iw_status_code i40iw_del_multiple_qhash(
memcpy(cm_info->loc_addr, child_listen_node->loc_addr, memcpy(cm_info->loc_addr, child_listen_node->loc_addr,
sizeof(cm_info->loc_addr)); sizeof(cm_info->loc_addr));
cm_info->vlan_id = child_listen_node->vlan_id; cm_info->vlan_id = child_listen_node->vlan_id;
ret = i40iw_manage_qhash(iwdev, cm_info, if (child_listen_node->qhash_set) {
I40IW_QHASH_TYPE_TCP_SYN, ret = i40iw_manage_qhash(iwdev, cm_info,
I40IW_QHASH_MANAGE_TYPE_DELETE, NULL, false); I40IW_QHASH_TYPE_TCP_SYN,
I40IW_QHASH_MANAGE_TYPE_DELETE,
NULL, false);
child_listen_node->qhash_set = false;
} else {
ret = I40IW_SUCCESS;
}
i40iw_debug(&iwdev->sc_dev, i40iw_debug(&iwdev->sc_dev,
I40IW_DEBUG_CM, I40IW_DEBUG_CM,
"freed pointer = %p\n", "freed pointer = %p\n",
@ -1687,6 +1693,7 @@ static enum i40iw_status_code i40iw_add_mqh_6(struct i40iw_device *iwdev,
I40IW_QHASH_MANAGE_TYPE_ADD, I40IW_QHASH_MANAGE_TYPE_ADD,
NULL, true); NULL, true);
if (!ret) { if (!ret) {
child_listen_node->qhash_set = true;
spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags); spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
list_add(&child_listen_node->child_listen_list, list_add(&child_listen_node->child_listen_list,
&cm_parent_listen_node->child_listen_list); &cm_parent_listen_node->child_listen_list);
@ -1765,6 +1772,7 @@ static enum i40iw_status_code i40iw_add_mqh_4(
NULL, NULL,
true); true);
if (!ret) { if (!ret) {
child_listen_node->qhash_set = true;
spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags); spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
list_add(&child_listen_node->child_listen_list, list_add(&child_listen_node->child_listen_list,
&cm_parent_listen_node->child_listen_list); &cm_parent_listen_node->child_listen_list);
@ -4129,6 +4137,73 @@ static void i40iw_cm_post_event(struct i40iw_cm_event *event)
queue_work(event->cm_node->cm_core->event_wq, &event->event_work); queue_work(event->cm_node->cm_core->event_wq, &event->event_work);
} }
/**
* i40iw_qhash_ctrl - enable/disable qhash for list
* @iwdev: device pointer
* @parent_listen_node: parent listen node
* @nfo: cm info node
* @ipaddr: Pointer to IPv4 or IPv6 address
* @ipv4: flag indicating IPv4 when true
* @ifup: flag indicating interface up when true
*
* Enables or disables the qhash for the node in the child
* listen list that matches ipaddr. If no matching IP was found
* it will allocate and add a new child listen node to the
* parent listen node. The listen_list_lock is assumed to be
* held when called.
*/
static void i40iw_qhash_ctrl(struct i40iw_device *iwdev,
struct i40iw_cm_listener *parent_listen_node,
struct i40iw_cm_info *nfo,
u32 *ipaddr, bool ipv4, bool ifup)
{
struct list_head *child_listen_list = &parent_listen_node->child_listen_list;
struct i40iw_cm_listener *child_listen_node;
struct list_head *pos, *tpos;
enum i40iw_status_code ret;
bool node_allocated = false;
enum i40iw_quad_hash_manage_type op =
ifup ? I40IW_QHASH_MANAGE_TYPE_ADD : I40IW_QHASH_MANAGE_TYPE_DELETE;
list_for_each_safe(pos, tpos, child_listen_list) {
child_listen_node =
list_entry(pos,
struct i40iw_cm_listener,
child_listen_list);
if (!memcmp(child_listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16))
goto set_qhash;
}
/* if not found then add a child listener if interface is going up */
if (!ifup)
return;
child_listen_node = kzalloc(sizeof(*child_listen_node), GFP_ATOMIC);
if (!child_listen_node)
return;
node_allocated = true;
memcpy(child_listen_node, parent_listen_node, sizeof(*child_listen_node));
memcpy(child_listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16);
set_qhash:
memcpy(nfo->loc_addr,
child_listen_node->loc_addr,
sizeof(nfo->loc_addr));
nfo->vlan_id = child_listen_node->vlan_id;
ret = i40iw_manage_qhash(iwdev, nfo,
I40IW_QHASH_TYPE_TCP_SYN,
op,
NULL, false);
if (!ret) {
child_listen_node->qhash_set = ifup;
if (node_allocated)
list_add(&child_listen_node->child_listen_list,
&parent_listen_node->child_listen_list);
} else if (node_allocated) {
kfree(child_listen_node);
}
}
/** /**
* i40iw_cm_disconnect_all - disconnect all connected qp's * i40iw_cm_disconnect_all - disconnect all connected qp's
* @iwdev: device pointer * @iwdev: device pointer
@ -4159,3 +4234,60 @@ void i40iw_cm_disconnect_all(struct i40iw_device *iwdev)
i40iw_rem_ref_cm_node(cm_node); i40iw_rem_ref_cm_node(cm_node);
} }
} }
/**
* i40iw_ifdown_notify - process an ifdown on an interface
* @iwdev: device pointer
* @ipaddr: Pointer to IPv4 or IPv6 address
* @ipv4: flag indicating IPv4 when true
* @ifup: flag indicating interface up when true
*/
void i40iw_if_notify(struct i40iw_device *iwdev, struct net_device *netdev,
u32 *ipaddr, bool ipv4, bool ifup)
{
struct i40iw_cm_core *cm_core = &iwdev->cm_core;
unsigned long flags;
struct i40iw_cm_listener *listen_node;
static const u32 ip_zero[4] = { 0, 0, 0, 0 };
struct i40iw_cm_info nfo;
u16 vlan_id = rdma_vlan_dev_vlan_id(netdev);
enum i40iw_status_code ret;
enum i40iw_quad_hash_manage_type op =
ifup ? I40IW_QHASH_MANAGE_TYPE_ADD : I40IW_QHASH_MANAGE_TYPE_DELETE;
/* Disable or enable qhash for listeners */
spin_lock_irqsave(&cm_core->listen_list_lock, flags);
list_for_each_entry(listen_node, &cm_core->listen_nodes, list) {
if (vlan_id == listen_node->vlan_id &&
(!memcmp(listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16) ||
!memcmp(listen_node->loc_addr, ip_zero, ipv4 ? 4 : 16))) {
memcpy(nfo.loc_addr, listen_node->loc_addr,
sizeof(nfo.loc_addr));
nfo.loc_port = listen_node->loc_port;
nfo.ipv4 = listen_node->ipv4;
nfo.vlan_id = listen_node->vlan_id;
nfo.user_pri = listen_node->user_pri;
if (!list_empty(&listen_node->child_listen_list)) {
i40iw_qhash_ctrl(iwdev,
listen_node,
&nfo,
ipaddr, ipv4, ifup);
} else if (memcmp(listen_node->loc_addr, ip_zero,
ipv4 ? 4 : 16)) {
ret = i40iw_manage_qhash(iwdev,
&nfo,
I40IW_QHASH_TYPE_TCP_SYN,
op,
NULL,
false);
if (!ret)
listen_node->qhash_set = ifup;
}
}
}
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
/* disconnect any connected qp's on ifdown */
if (!ifup)
i40iw_cm_disconnect_all(iwdev);
}

View File

@ -444,5 +444,7 @@ int i40iw_arp_table(struct i40iw_device *iwdev,
u8 *mac_addr, u8 *mac_addr,
u32 action); u32 action);
void i40iw_if_notify(struct i40iw_device *iwdev, struct net_device *netdev,
u32 *ipaddr, bool ipv4, bool ifup);
void i40iw_cm_disconnect_all(struct i40iw_device *iwdev); void i40iw_cm_disconnect_all(struct i40iw_device *iwdev);
#endif /* I40IW_CM_H */ #endif /* I40IW_CM_H */

View File

@ -153,6 +153,7 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
struct i40iw_device *iwdev; struct i40iw_device *iwdev;
struct i40iw_handler *hdl; struct i40iw_handler *hdl;
u32 local_ipaddr; u32 local_ipaddr;
u32 action = I40IW_ARP_ADD;
hdl = i40iw_find_netdev(event_netdev); hdl = i40iw_find_netdev(event_netdev);
if (!hdl) if (!hdl)
@ -164,44 +165,25 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
if (netdev != event_netdev) if (netdev != event_netdev)
return NOTIFY_DONE; return NOTIFY_DONE;
if (upper_dev)
local_ipaddr = ntohl(
((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
else
local_ipaddr = ntohl(ifa->ifa_address);
switch (event) { switch (event) {
case NETDEV_DOWN: case NETDEV_DOWN:
if (upper_dev) action = I40IW_ARP_DELETE;
local_ipaddr = ntohl( /* Fall through */
((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
else
local_ipaddr = ntohl(ifa->ifa_address);
i40iw_manage_arp_cache(iwdev,
netdev->dev_addr,
&local_ipaddr,
true,
I40IW_ARP_DELETE);
return NOTIFY_OK;
case NETDEV_UP: case NETDEV_UP:
if (upper_dev) /* Fall through */
local_ipaddr = ntohl(
((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
else
local_ipaddr = ntohl(ifa->ifa_address);
i40iw_manage_arp_cache(iwdev,
netdev->dev_addr,
&local_ipaddr,
true,
I40IW_ARP_ADD);
break;
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
/* Add the address to the IP table */
if (upper_dev)
local_ipaddr = ntohl(
((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
else
local_ipaddr = ntohl(ifa->ifa_address);
i40iw_manage_arp_cache(iwdev, i40iw_manage_arp_cache(iwdev,
netdev->dev_addr, netdev->dev_addr,
&local_ipaddr, &local_ipaddr,
true, true,
I40IW_ARP_ADD); action);
i40iw_if_notify(iwdev, netdev, &local_ipaddr, true,
(action == I40IW_ARP_ADD) ? true : false);
break; break;
default: default:
break; break;
@ -225,6 +207,7 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
struct i40iw_device *iwdev; struct i40iw_device *iwdev;
struct i40iw_handler *hdl; struct i40iw_handler *hdl;
u32 local_ipaddr6[4]; u32 local_ipaddr6[4];
u32 action = I40IW_ARP_ADD;
hdl = i40iw_find_netdev(event_netdev); hdl = i40iw_find_netdev(event_netdev);
if (!hdl) if (!hdl)
@ -235,24 +218,21 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
if (netdev != event_netdev) if (netdev != event_netdev)
return NOTIFY_DONE; return NOTIFY_DONE;
i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
switch (event) { switch (event) {
case NETDEV_DOWN: case NETDEV_DOWN:
i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32); action = I40IW_ARP_DELETE;
i40iw_manage_arp_cache(iwdev, /* Fall through */
netdev->dev_addr,
local_ipaddr6,
false,
I40IW_ARP_DELETE);
return NOTIFY_OK;
case NETDEV_UP: case NETDEV_UP:
/* Fall through */ /* Fall through */
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
i40iw_manage_arp_cache(iwdev, i40iw_manage_arp_cache(iwdev,
netdev->dev_addr, netdev->dev_addr,
local_ipaddr6, local_ipaddr6,
false, false,
I40IW_ARP_ADD); action);
i40iw_if_notify(iwdev, netdev, local_ipaddr6, false,
(action == I40IW_ARP_ADD) ? true : false);
break; break;
default: default:
break; break;