Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5e74b61 authored by Mustafa Ismail's avatar Mustafa Ismail Committed by Doug Ledford
Browse files

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: default avatarMustafa Ismail <mustafa.ismail@intel.com>
Signed-off-by: default avatarShiraz Saleem <shiraz.saleem@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent d5965934
Loading
Loading
Loading
Loading
+135 −3
Original line number Diff line number Diff line
@@ -1556,9 +1556,15 @@ static enum i40iw_status_code i40iw_del_multiple_qhash(
		memcpy(cm_info->loc_addr, child_listen_node->loc_addr,
		       sizeof(cm_info->loc_addr));
		cm_info->vlan_id = child_listen_node->vlan_id;
		if (child_listen_node->qhash_set) {
			ret = i40iw_manage_qhash(iwdev, cm_info,
						 I40IW_QHASH_TYPE_TCP_SYN,
					 I40IW_QHASH_MANAGE_TYPE_DELETE, NULL, false);
						 I40IW_QHASH_MANAGE_TYPE_DELETE,
						 NULL, false);
			child_listen_node->qhash_set = false;
		} else {
			ret = I40IW_SUCCESS;
		}
		i40iw_debug(&iwdev->sc_dev,
			    I40IW_DEBUG_CM,
			    "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,
							 NULL, true);
				if (!ret) {
					child_listen_node->qhash_set = true;
					spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
					list_add(&child_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,
							 true);
				if (!ret) {
					child_listen_node->qhash_set = true;
					spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
					list_add(&child_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);
}

/**
 * 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
 * @iwdev: device pointer
@@ -4159,3 +4234,60 @@ void i40iw_cm_disconnect_all(struct i40iw_device *iwdev)
		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);
}
+2 −0
Original line number Diff line number Diff line
@@ -444,5 +444,7 @@ int i40iw_arp_table(struct i40iw_device *iwdev,
		    u8 *mac_addr,
		    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);
#endif /* I40IW_CM_H */
+19 −39
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
	struct i40iw_device *iwdev;
	struct i40iw_handler *hdl;
	u32 local_ipaddr;
	u32 action = I40IW_ARP_ADD;

	hdl = i40iw_find_netdev(event_netdev);
	if (!hdl)
@@ -164,44 +165,25 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
	if (netdev != event_netdev)
		return NOTIFY_DONE;

	switch (event) {
	case NETDEV_DOWN:
	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,
				       netdev->dev_addr,
				       &local_ipaddr,
				       true,
				       I40IW_ARP_DELETE);
		return NOTIFY_OK;
	switch (event) {
	case NETDEV_DOWN:
		action = I40IW_ARP_DELETE;
		/* Fall through */
	case NETDEV_UP:
		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,
				       netdev->dev_addr,
				       &local_ipaddr,
				       true,
				       I40IW_ARP_ADD);
		break;
		/* Fall through */
	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,
				       netdev->dev_addr,
				       &local_ipaddr,
				       true,
				       I40IW_ARP_ADD);
				       action);
		i40iw_if_notify(iwdev, netdev, &local_ipaddr, true,
				(action == I40IW_ARP_ADD) ? true : false);
		break;
	default:
		break;
@@ -225,6 +207,7 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
	struct i40iw_device *iwdev;
	struct i40iw_handler *hdl;
	u32 local_ipaddr6[4];
	u32 action = I40IW_ARP_ADD;

	hdl = i40iw_find_netdev(event_netdev);
	if (!hdl)
@@ -235,24 +218,21 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
	if (netdev != event_netdev)
		return NOTIFY_DONE;

	i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
	switch (event) {
	case NETDEV_DOWN:
		i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
		i40iw_manage_arp_cache(iwdev,
				       netdev->dev_addr,
				       local_ipaddr6,
				       false,
				       I40IW_ARP_DELETE);
		return NOTIFY_OK;
		action = I40IW_ARP_DELETE;
		/* Fall through */
	case NETDEV_UP:
		/* Fall through */
	case NETDEV_CHANGEADDR:
		i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
		i40iw_manage_arp_cache(iwdev,
				       netdev->dev_addr,
				       local_ipaddr6,
				       false,
				       I40IW_ARP_ADD);
				       action);
		i40iw_if_notify(iwdev, netdev, local_ipaddr6, false,
				(action == I40IW_ARP_ADD) ? true : false);
		break;
	default:
		break;