@@ -4037,18 +4037,19 @@ int irdma_destroy_listen(struct iw_cm_id *cm_id)
}
/**
- * irdma_teardown_list_prep - add conn nodes slated for tear down to list
+ * irdma_iw_teardown_list_prep - add conn nodes slated for tear
+ * down to list
* @cm_core: cm's core
* @teardown_list: a list to which cm_node will be selected
* @ipaddr: pointer to ip address
* @nfo: pointer to cm_info structure instance
* @disconnect_all: flag indicating disconnect all QPs
*/
-static void irdma_teardown_list_prep(struct irdma_cm_core *cm_core,
- struct list_head *teardown_list,
- u32 *ipaddr,
- struct irdma_cm_info *nfo,
- bool disconnect_all)
+static void irdma_iw_teardown_list_prep(struct irdma_cm_core *cm_core,
+ struct list_head *teardown_list,
+ u32 *ipaddr,
+ struct irdma_cm_info *nfo,
+ bool disconnect_all)
{
struct irdma_cm_node *cm_node;
int bkt;
@@ -4062,6 +4063,73 @@ static void irdma_teardown_list_prep(struct irdma_cm_core *cm_core,
}
}
+static inline bool irdma_ip_vlan_match(u32 *ip1, u16 vlan_id1,
+ bool check_vlan, u32 *ip2,
+ u16 vlan_id2, bool ipv4)
+{
+ return (!check_vlan || vlan_id1 == vlan_id2) &&
+ !memcmp(ip1, ip2, ipv4 ? 4 : 16);
+}
+
+/**
+ * irdma_roce_teardown_list_prep - add conn nodes slated for
+ * tear down to list
+ * @iwdev: RDMA device
+ * @teardown_list: a list to which cm_node will be selected
+ * @ipaddr: pointer to ip address
+ * @nfo: pointer to cm_info structure instance
+ * @disconnect_all: flag indicating disconnect all QPs
+ */
+static void irdma_roce_teardown_list_prep(struct irdma_device *iwdev,
+ struct list_head *teardown_list,
+ u32 *ipaddr,
+ struct irdma_cm_info *nfo,
+ bool disconnect_all)
+{
+ struct irdma_sc_vsi *vsi = &iwdev->vsi;
+ struct irdma_sc_qp *sc_qp;
+ struct list_head *list_node;
+ struct irdma_qp *qp;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
+ mutex_lock(&vsi->qos[i].qos_mutex);
+ list_for_each (list_node, &vsi->qos[i].qplist) {
+ u32 qp_ip[4];
+
+ sc_qp = container_of(list_node, struct irdma_sc_qp,
+ list);
+ if (sc_qp->qp_uk.qp_type != IRDMA_QP_TYPE_ROCE_RC)
+ continue;
+
+ qp = sc_qp->qp_uk.back_qp;
+ if (!disconnect_all) {
+ if (nfo->ipv4)
+ qp_ip[0] = qp->udp_info.local_ipaddr[3];
+ else
+ memcpy(qp_ip,
+ &qp->udp_info.local_ipaddr[0],
+ sizeof(qp_ip));
+ }
+
+ if (disconnect_all ||
+ irdma_ip_vlan_match(qp_ip,
+ qp->udp_info.vlan_tag & VLAN_VID_MASK,
+ qp->udp_info.insert_vlan_tag,
+ ipaddr, nfo->vlan_id, nfo->ipv4)) {
+ spin_lock_irqsave(&iwdev->rf->qptable_lock, flags);
+ if (iwdev->rf->qp_table[sc_qp->qp_uk.qp_id]) {
+ irdma_qp_add_ref(&qp->ibqp);
+ list_add(&qp->teardown_entry, teardown_list);
+ }
+ spin_unlock_irqrestore(&iwdev->rf->qptable_lock, flags);
+ }
+ }
+ mutex_unlock(&vsi->qos[i].qos_mutex);
+ }
+}
+
/**
* irdma_cm_event_connected - handle connected active node
* @event: the info for cm_node of connection
@@ -4232,22 +4300,36 @@ void irdma_cm_teardown_connections(struct irdma_device *iwdev, u32 *ipaddr,
struct irdma_cm_node *cm_node;
struct list_head teardown_list;
struct ib_qp_attr attr;
+ struct irdma_qp *qp;
INIT_LIST_HEAD(&teardown_list);
rcu_read_lock();
- irdma_teardown_list_prep(cm_core, &teardown_list, ipaddr, nfo, disconnect_all);
+ irdma_iw_teardown_list_prep(cm_core, &teardown_list, ipaddr, nfo, disconnect_all);
rcu_read_unlock();
+ attr.qp_state = IB_QPS_ERR;
list_for_each_safe (list_node, list_core_temp, &teardown_list) {
cm_node = container_of(list_node, struct irdma_cm_node,
teardown_entry);
- attr.qp_state = IB_QPS_ERR;
irdma_modify_qp(&cm_node->iwqp->ibqp, &attr, IB_QP_STATE, NULL);
if (iwdev->rf->reset)
irdma_cm_disconn(cm_node->iwqp);
irdma_rem_ref_cm_node(cm_node);
}
+
+ if (!iwdev->roce_mode)
+ return;
+
+ INIT_LIST_HEAD(&teardown_list);
+ irdma_roce_teardown_list_prep(iwdev, &teardown_list, ipaddr, nfo, disconnect_all);
+
+ list_for_each_safe (list_node, list_core_temp, &teardown_list) {
+ qp = container_of(list_node, struct irdma_qp, teardown_entry);
+ irdma_modify_qp_roce(&qp->ibqp, &attr, IB_QP_STATE, NULL);
+ irdma_ib_qp_event(qp, IRDMA_QP_EVENT_CATASTROPHIC);
+ irdma_qp_rem_ref(&qp->ibqp);
+ }
}
/**
@@ -4316,12 +4398,12 @@ static void irdma_qhash_ctrl(struct irdma_device *iwdev,
/**
* irdma_if_notify - process an ifdown on an interface
* @iwdev: device pointer
- * @netdev: network device structure
+ * @vlan_id:VLAN ID of the netdev
* @ipaddr: Pointer to IPv4 or IPv6 address
* @ipv4: flag indicating IPv4 when true
* @ifup: flag indicating interface up when true
*/
-void irdma_if_notify(struct irdma_device *iwdev, struct net_device *netdev,
+void irdma_if_notify(struct irdma_device *iwdev, u16 vlan_id,
u32 *ipaddr, bool ipv4, bool ifup)
{
struct irdma_cm_core *cm_core = &iwdev->cm_core;
@@ -4329,7 +4411,6 @@ void irdma_if_notify(struct irdma_device *iwdev, struct net_device *netdev,
struct irdma_cm_listener *listen_node;
static const u32 ip_zero[4] = { 0, 0, 0, 0 };
struct irdma_cm_info nfo = {};
- u16 vlan_id = rdma_vlan_dev_vlan_id(netdev);
enum irdma_quad_hash_manage_type op = ifup ?
IRDMA_QHASH_MANAGE_TYPE_ADD :
IRDMA_QHASH_MANAGE_TYPE_DELETE;
@@ -4367,3 +4448,13 @@ void irdma_if_notify(struct irdma_device *iwdev, struct net_device *netdev,
if (!ifup)
irdma_cm_teardown_connections(iwdev, ipaddr, &nfo, false);
}
+
+void irdma_if_notify_worker(struct work_struct *work)
+{
+ struct if_notify_work *iwork =
+ container_of(work, struct if_notify_work, work);
+
+ irdma_if_notify(iwork->iwdev, iwork->vlan_id, iwork->ipaddr,
+ iwork->ipv4, iwork->ifup);
+ kfree(iwork);
+}
@@ -397,17 +397,13 @@ int irdma_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param);
int irdma_create_listen(struct iw_cm_id *cm_id, int backlog);
int irdma_destroy_listen(struct iw_cm_id *cm_id);
int irdma_add_arp(struct irdma_pci_f *rf, u32 *ip, bool ipv4, const u8 *mac);
-void irdma_cm_teardown_connections(struct irdma_device *iwdev, u32 *ipaddr,
- struct irdma_cm_info *nfo,
- bool disconnect_all);
+void irdma_if_notify_worker(struct work_struct *work);
int irdma_cm_start(struct irdma_device *dev);
int irdma_cm_stop(struct irdma_device *dev);
bool irdma_ipv4_is_lpb(u32 loc_addr, u32 rem_addr);
bool irdma_ipv6_is_lpb(u32 *loc_addr, u32 *rem_addr);
int irdma_arp_table(struct irdma_pci_f *rf, u32 *ip_addr, bool ipv4,
const u8 *mac_addr, u32 action);
-void irdma_if_notify(struct irdma_device *iwdev, struct net_device *netdev,
- u32 *ipaddr, bool ipv4, bool ifup);
bool irdma_port_in_use(struct irdma_cm_core *cm_core, u16 port);
void irdma_send_ack(struct irdma_cm_node *cm_node);
void irdma_lpb_nop(struct irdma_sc_qp *qp);
@@ -140,6 +140,25 @@ static void irdma_gid_change_event(struct ib_device *ibdev)
ib_dispatch_event(&ib_event);
}
+static void irdma_if_notify_sched(struct irdma_device *iwdev,
+ struct net_device *netdev,
+ u32 *ipaddr, bool ipv4, bool ifup)
+{
+ struct if_notify_work *work;
+
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return;
+ work->iwdev = iwdev;
+ work->vlan_id = rdma_vlan_dev_vlan_id(netdev);
+ work->ipv4 = ipv4;
+ work->ifup = ifup;
+ memcpy(work->ipaddr, ipaddr, ipv4 ? 4 : 16);
+
+ INIT_WORK(&work->work, irdma_if_notify_worker);
+ queue_work(iwdev->cleanup_wq, &work->work);
+}
+
/**
* irdma_inetaddr_event - system notifier for ipv4 addr events
* @notifier: not used
@@ -172,13 +191,13 @@ int irdma_inetaddr_event(struct notifier_block *notifier, unsigned long event,
case NETDEV_DOWN:
irdma_manage_arp_cache(iwdev->rf, real_dev->dev_addr,
&local_ipaddr, true, IRDMA_ARP_DELETE);
- irdma_if_notify(iwdev, real_dev, &local_ipaddr, true, false);
+ irdma_if_notify_sched(iwdev, netdev, &local_ipaddr, true, false);
irdma_gid_change_event(&iwdev->ibdev);
break;
case NETDEV_UP:
case NETDEV_CHANGEADDR:
irdma_add_arp(iwdev->rf, &local_ipaddr, true, real_dev->dev_addr);
- irdma_if_notify(iwdev, real_dev, &local_ipaddr, true, true);
+ irdma_if_notify_sched(iwdev, netdev, &local_ipaddr, true, true);
irdma_gid_change_event(&iwdev->ibdev);
break;
default:
@@ -222,14 +241,14 @@ int irdma_inet6addr_event(struct notifier_block *notifier, unsigned long event,
case NETDEV_DOWN:
irdma_manage_arp_cache(iwdev->rf, real_dev->dev_addr,
local_ipaddr6, false, IRDMA_ARP_DELETE);
- irdma_if_notify(iwdev, real_dev, local_ipaddr6, false, false);
+ irdma_if_notify_sched(iwdev, netdev, local_ipaddr6, false, false);
irdma_gid_change_event(&iwdev->ibdev);
break;
case NETDEV_UP:
case NETDEV_CHANGEADDR:
irdma_add_arp(iwdev->rf, local_ipaddr6, false,
real_dev->dev_addr);
- irdma_if_notify(iwdev, real_dev, local_ipaddr6, false, true);
+ irdma_if_notify_sched(iwdev, netdev, local_ipaddr6, false, true);
irdma_gid_change_event(&iwdev->ibdev);
break;
default:
@@ -142,6 +142,15 @@ struct disconn_work {
struct iw_cm_id;
+struct if_notify_work {
+ struct work_struct work;
+ struct irdma_device *iwdev;
+ u32 ipaddr[4];
+ u16 vlan_id;
+ bool ipv4:1;
+ bool ifup:1;
+};
+
struct irdma_qp_kmode {
struct irdma_dma_mem dma_mem;
struct irdma_sq_uk_wr_trk_info *sq_wrid_mem;