diff options
author | Mustafa Ismail <mustafa.ismail@intel.com> | 2016-11-30 16:07:30 -0500 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2016-12-05 16:09:41 -0500 |
commit | e5e74b61b16503acbd914f673b783fa2a1532a64 (patch) | |
tree | c9b336820d5c29cf0719f1602977a9aa9c6f00a8 | |
parent | d59659340c61e777208524f77c268fe6edc6fe37 (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.c | 138 | ||||
-rw-r--r-- | drivers/infiniband/hw/i40iw/i40iw_cm.h | 2 | ||||
-rw-r--r-- | drivers/infiniband/hw/i40iw/i40iw_utils.c | 58 |
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 | */ | ||
4155 | static 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 | |||
4188 | set_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 | */ | ||
4245 | void 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 | ||
447 | void i40iw_if_notify(struct i40iw_device *iwdev, struct net_device *netdev, | ||
448 | u32 *ipaddr, bool ipv4, bool ifup); | ||
447 | void i40iw_cm_disconnect_all(struct i40iw_device *iwdev); | 449 | void 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; |