aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMustafa Ismail <mustafa.ismail@intel.com>2016-11-30 16:07:30 -0500
committerDoug Ledford <dledford@redhat.com>2016-12-05 16:09:41 -0500
commite5e74b61b16503acbd914f673b783fa2a1532a64 (patch)
treec9b336820d5c29cf0719f1602977a9aa9c6f00a8
parentd59659340c61e777208524f77c268fe6edc6fe37 (diff)
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>
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_cm.c138
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_cm.h2
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_utils.c58
3 files changed, 156 insertions, 42 deletions
diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c
index 11ef0b09c843..93ae764fc44e 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_cm.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c
@@ -1556,9 +1556,15 @@ static enum i40iw_status_code i40iw_del_multiple_qhash(
1556 memcpy(cm_info->loc_addr, child_listen_node->loc_addr, 1556 memcpy(cm_info->loc_addr, child_listen_node->loc_addr,
1557 sizeof(cm_info->loc_addr)); 1557 sizeof(cm_info->loc_addr));
1558 cm_info->vlan_id = child_listen_node->vlan_id; 1558 cm_info->vlan_id = child_listen_node->vlan_id;
1559 ret = i40iw_manage_qhash(iwdev, cm_info, 1559 if (child_listen_node->qhash_set) {
1560 I40IW_QHASH_TYPE_TCP_SYN, 1560 ret = i40iw_manage_qhash(iwdev, cm_info,
1561 I40IW_QHASH_MANAGE_TYPE_DELETE, NULL, false); 1561 I40IW_QHASH_TYPE_TCP_SYN,
1562 I40IW_QHASH_MANAGE_TYPE_DELETE,
1563 NULL, false);
1564 child_listen_node->qhash_set = false;
1565 } else {
1566 ret = I40IW_SUCCESS;
1567 }
1562 i40iw_debug(&iwdev->sc_dev, 1568 i40iw_debug(&iwdev->sc_dev,
1563 I40IW_DEBUG_CM, 1569 I40IW_DEBUG_CM,
1564 "freed pointer = %p\n", 1570 "freed pointer = %p\n",
@@ -1687,6 +1693,7 @@ static enum i40iw_status_code i40iw_add_mqh_6(struct i40iw_device *iwdev,
1687 I40IW_QHASH_MANAGE_TYPE_ADD, 1693 I40IW_QHASH_MANAGE_TYPE_ADD,
1688 NULL, true); 1694 NULL, true);
1689 if (!ret) { 1695 if (!ret) {
1696 child_listen_node->qhash_set = true;
1690 spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags); 1697 spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
1691 list_add(&child_listen_node->child_listen_list, 1698 list_add(&child_listen_node->child_listen_list,
1692 &cm_parent_listen_node->child_listen_list); 1699 &cm_parent_listen_node->child_listen_list);
@@ -1765,6 +1772,7 @@ static enum i40iw_status_code i40iw_add_mqh_4(
1765 NULL, 1772 NULL,
1766 true); 1773 true);
1767 if (!ret) { 1774 if (!ret) {
1775 child_listen_node->qhash_set = true;
1768 spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags); 1776 spin_lock_irqsave(&iwdev->cm_core.listen_list_lock, flags);
1769 list_add(&child_listen_node->child_listen_list, 1777 list_add(&child_listen_node->child_listen_list,
1770 &cm_parent_listen_node->child_listen_list); 1778 &cm_parent_listen_node->child_listen_list);
@@ -4130,6 +4138,73 @@ static void i40iw_cm_post_event(struct i40iw_cm_event *event)
4130} 4138}
4131 4139
4132/** 4140/**
4141 * i40iw_qhash_ctrl - enable/disable qhash for list
4142 * @iwdev: device pointer
4143 * @parent_listen_node: parent listen node
4144 * @nfo: cm info node
4145 * @ipaddr: Pointer to IPv4 or IPv6 address
4146 * @ipv4: flag indicating IPv4 when true
4147 * @ifup: flag indicating interface up when true
4148 *
4149 * Enables or disables the qhash for the node in the child
4150 * listen list that matches ipaddr. If no matching IP was found
4151 * it will allocate and add a new child listen node to the
4152 * parent listen node. The listen_list_lock is assumed to be
4153 * held when called.
4154 */
4155static void i40iw_qhash_ctrl(struct i40iw_device *iwdev,
4156 struct i40iw_cm_listener *parent_listen_node,
4157 struct i40iw_cm_info *nfo,
4158 u32 *ipaddr, bool ipv4, bool ifup)
4159{
4160 struct list_head *child_listen_list = &parent_listen_node->child_listen_list;
4161 struct i40iw_cm_listener *child_listen_node;
4162 struct list_head *pos, *tpos;
4163 enum i40iw_status_code ret;
4164 bool node_allocated = false;
4165 enum i40iw_quad_hash_manage_type op =
4166 ifup ? I40IW_QHASH_MANAGE_TYPE_ADD : I40IW_QHASH_MANAGE_TYPE_DELETE;
4167
4168 list_for_each_safe(pos, tpos, child_listen_list) {
4169 child_listen_node =
4170 list_entry(pos,
4171 struct i40iw_cm_listener,
4172 child_listen_list);
4173 if (!memcmp(child_listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16))
4174 goto set_qhash;
4175 }
4176
4177 /* if not found then add a child listener if interface is going up */
4178 if (!ifup)
4179 return;
4180 child_listen_node = kzalloc(sizeof(*child_listen_node), GFP_ATOMIC);
4181 if (!child_listen_node)
4182 return;
4183 node_allocated = true;
4184 memcpy(child_listen_node, parent_listen_node, sizeof(*child_listen_node));
4185
4186 memcpy(child_listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16);
4187
4188set_qhash:
4189 memcpy(nfo->loc_addr,
4190 child_listen_node->loc_addr,
4191 sizeof(nfo->loc_addr));
4192 nfo->vlan_id = child_listen_node->vlan_id;
4193 ret = i40iw_manage_qhash(iwdev, nfo,
4194 I40IW_QHASH_TYPE_TCP_SYN,
4195 op,
4196 NULL, false);
4197 if (!ret) {
4198 child_listen_node->qhash_set = ifup;
4199 if (node_allocated)
4200 list_add(&child_listen_node->child_listen_list,
4201 &parent_listen_node->child_listen_list);
4202 } else if (node_allocated) {
4203 kfree(child_listen_node);
4204 }
4205}
4206
4207/**
4133 * i40iw_cm_disconnect_all - disconnect all connected qp's 4208 * i40iw_cm_disconnect_all - disconnect all connected qp's
4134 * @iwdev: device pointer 4209 * @iwdev: device pointer
4135 */ 4210 */
@@ -4159,3 +4234,60 @@ void i40iw_cm_disconnect_all(struct i40iw_device *iwdev)
4159 i40iw_rem_ref_cm_node(cm_node); 4234 i40iw_rem_ref_cm_node(cm_node);
4160 } 4235 }
4161} 4236}
4237
4238/**
4239 * i40iw_ifdown_notify - process an ifdown on an interface
4240 * @iwdev: device pointer
4241 * @ipaddr: Pointer to IPv4 or IPv6 address
4242 * @ipv4: flag indicating IPv4 when true
4243 * @ifup: flag indicating interface up when true
4244 */
4245void i40iw_if_notify(struct i40iw_device *iwdev, struct net_device *netdev,
4246 u32 *ipaddr, bool ipv4, bool ifup)
4247{
4248 struct i40iw_cm_core *cm_core = &iwdev->cm_core;
4249 unsigned long flags;
4250 struct i40iw_cm_listener *listen_node;
4251 static const u32 ip_zero[4] = { 0, 0, 0, 0 };
4252 struct i40iw_cm_info nfo;
4253 u16 vlan_id = rdma_vlan_dev_vlan_id(netdev);
4254 enum i40iw_status_code ret;
4255 enum i40iw_quad_hash_manage_type op =
4256 ifup ? I40IW_QHASH_MANAGE_TYPE_ADD : I40IW_QHASH_MANAGE_TYPE_DELETE;
4257
4258 /* Disable or enable qhash for listeners */
4259 spin_lock_irqsave(&cm_core->listen_list_lock, flags);
4260 list_for_each_entry(listen_node, &cm_core->listen_nodes, list) {
4261 if (vlan_id == listen_node->vlan_id &&
4262 (!memcmp(listen_node->loc_addr, ipaddr, ipv4 ? 4 : 16) ||
4263 !memcmp(listen_node->loc_addr, ip_zero, ipv4 ? 4 : 16))) {
4264 memcpy(nfo.loc_addr, listen_node->loc_addr,
4265 sizeof(nfo.loc_addr));
4266 nfo.loc_port = listen_node->loc_port;
4267 nfo.ipv4 = listen_node->ipv4;
4268 nfo.vlan_id = listen_node->vlan_id;
4269 nfo.user_pri = listen_node->user_pri;
4270 if (!list_empty(&listen_node->child_listen_list)) {
4271 i40iw_qhash_ctrl(iwdev,
4272 listen_node,
4273 &nfo,
4274 ipaddr, ipv4, ifup);
4275 } else if (memcmp(listen_node->loc_addr, ip_zero,
4276 ipv4 ? 4 : 16)) {
4277 ret = i40iw_manage_qhash(iwdev,
4278 &nfo,
4279 I40IW_QHASH_TYPE_TCP_SYN,
4280 op,
4281 NULL,
4282 false);
4283 if (!ret)
4284 listen_node->qhash_set = ifup;
4285 }
4286 }
4287 }
4288 spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
4289
4290 /* disconnect any connected qp's on ifdown */
4291 if (!ifup)
4292 i40iw_cm_disconnect_all(iwdev);
4293}
diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.h b/drivers/infiniband/hw/i40iw/i40iw_cm.h
index 0381b7f5e20d..49ed7a52a84d 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_cm.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_cm.h
@@ -444,5 +444,7 @@ int i40iw_arp_table(struct i40iw_device *iwdev,
444 u8 *mac_addr, 444 u8 *mac_addr,
445 u32 action); 445 u32 action);
446 446
447void i40iw_if_notify(struct i40iw_device *iwdev, struct net_device *netdev,
448 u32 *ipaddr, bool ipv4, bool ifup);
447void i40iw_cm_disconnect_all(struct i40iw_device *iwdev); 449void i40iw_cm_disconnect_all(struct i40iw_device *iwdev);
448#endif /* I40IW_CM_H */ 450#endif /* I40IW_CM_H */
diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c
index 58151280828d..641f00f3dda1 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_utils.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c
@@ -153,6 +153,7 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
153 struct i40iw_device *iwdev; 153 struct i40iw_device *iwdev;
154 struct i40iw_handler *hdl; 154 struct i40iw_handler *hdl;
155 u32 local_ipaddr; 155 u32 local_ipaddr;
156 u32 action = I40IW_ARP_ADD;
156 157
157 hdl = i40iw_find_netdev(event_netdev); 158 hdl = i40iw_find_netdev(event_netdev);
158 if (!hdl) 159 if (!hdl)
@@ -164,44 +165,25 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
164 if (netdev != event_netdev) 165 if (netdev != event_netdev)
165 return NOTIFY_DONE; 166 return NOTIFY_DONE;
166 167
168 if (upper_dev)
169 local_ipaddr = ntohl(
170 ((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
171 else
172 local_ipaddr = ntohl(ifa->ifa_address);
167 switch (event) { 173 switch (event) {
168 case NETDEV_DOWN: 174 case NETDEV_DOWN:
169 if (upper_dev) 175 action = I40IW_ARP_DELETE;
170 local_ipaddr = ntohl( 176 /* Fall through */
171 ((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
172 else
173 local_ipaddr = ntohl(ifa->ifa_address);
174 i40iw_manage_arp_cache(iwdev,
175 netdev->dev_addr,
176 &local_ipaddr,
177 true,
178 I40IW_ARP_DELETE);
179 return NOTIFY_OK;
180 case NETDEV_UP: 177 case NETDEV_UP:
181 if (upper_dev) 178 /* Fall through */
182 local_ipaddr = ntohl(
183 ((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
184 else
185 local_ipaddr = ntohl(ifa->ifa_address);
186 i40iw_manage_arp_cache(iwdev,
187 netdev->dev_addr,
188 &local_ipaddr,
189 true,
190 I40IW_ARP_ADD);
191 break;
192 case NETDEV_CHANGEADDR: 179 case NETDEV_CHANGEADDR:
193 /* Add the address to the IP table */
194 if (upper_dev)
195 local_ipaddr = ntohl(
196 ((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address);
197 else
198 local_ipaddr = ntohl(ifa->ifa_address);
199
200 i40iw_manage_arp_cache(iwdev, 180 i40iw_manage_arp_cache(iwdev,
201 netdev->dev_addr, 181 netdev->dev_addr,
202 &local_ipaddr, 182 &local_ipaddr,
203 true, 183 true,
204 I40IW_ARP_ADD); 184 action);
185 i40iw_if_notify(iwdev, netdev, &local_ipaddr, true,
186 (action == I40IW_ARP_ADD) ? true : false);
205 break; 187 break;
206 default: 188 default:
207 break; 189 break;
@@ -225,6 +207,7 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
225 struct i40iw_device *iwdev; 207 struct i40iw_device *iwdev;
226 struct i40iw_handler *hdl; 208 struct i40iw_handler *hdl;
227 u32 local_ipaddr6[4]; 209 u32 local_ipaddr6[4];
210 u32 action = I40IW_ARP_ADD;
228 211
229 hdl = i40iw_find_netdev(event_netdev); 212 hdl = i40iw_find_netdev(event_netdev);
230 if (!hdl) 213 if (!hdl)
@@ -235,24 +218,21 @@ int i40iw_inet6addr_event(struct notifier_block *notifier,
235 if (netdev != event_netdev) 218 if (netdev != event_netdev)
236 return NOTIFY_DONE; 219 return NOTIFY_DONE;
237 220
221 i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
238 switch (event) { 222 switch (event) {
239 case NETDEV_DOWN: 223 case NETDEV_DOWN:
240 i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32); 224 action = I40IW_ARP_DELETE;
241 i40iw_manage_arp_cache(iwdev, 225 /* Fall through */
242 netdev->dev_addr,
243 local_ipaddr6,
244 false,
245 I40IW_ARP_DELETE);
246 return NOTIFY_OK;
247 case NETDEV_UP: 226 case NETDEV_UP:
248 /* Fall through */ 227 /* Fall through */
249 case NETDEV_CHANGEADDR: 228 case NETDEV_CHANGEADDR:
250 i40iw_copy_ip_ntohl(local_ipaddr6, ifa->addr.in6_u.u6_addr32);
251 i40iw_manage_arp_cache(iwdev, 229 i40iw_manage_arp_cache(iwdev,
252 netdev->dev_addr, 230 netdev->dev_addr,
253 local_ipaddr6, 231 local_ipaddr6,
254 false, 232 false,
255 I40IW_ARP_ADD); 233 action);
234 i40iw_if_notify(iwdev, netdev, local_ipaddr6, false,
235 (action == I40IW_ARP_ADD) ? true : false);
256 break; 236 break;
257 default: 237 default:
258 break; 238 break;