diff options
author | Rajesh Borundia <rajesh.borundia@qlogic.com> | 2013-03-29 01:46:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-29 15:51:06 -0400 |
commit | e8b508ef71fb70ec761086532716b19d3c4773e5 (patch) | |
tree | 22e27a6c8369ffff72375ead0a120bbf16b9f9a0 /drivers/net/ethernet/qlogic/qlcnic | |
parent | 7cb03b2347d5edace4fb8e7dd9d6c3889368a179 (diff) |
qlcnic: Support atomic commands
o VFs might get scheduled out after sending a command to a PF
and scheduled in after receiving a response. Implement a
worker thread to handle atomic commands.
Signed-off-by: Manish Chopra <manish.chopra@qlogic.com>
Signed-off-by: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 47 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 130 |
5 files changed, 183 insertions, 9 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index e5f7695e8443..c0b3ccf04ed2 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | |||
@@ -1013,6 +1013,7 @@ struct qlcnic_adapter { | |||
1013 | 1013 | ||
1014 | struct qlcnic_filter_hash fhash; | 1014 | struct qlcnic_filter_hash fhash; |
1015 | struct qlcnic_filter_hash rx_fhash; | 1015 | struct qlcnic_filter_hash rx_fhash; |
1016 | struct list_head vf_mc_list; | ||
1016 | 1017 | ||
1017 | spinlock_t tx_clean_lock; | 1018 | spinlock_t tx_clean_lock; |
1018 | spinlock_t mac_learn_lock; | 1019 | spinlock_t mac_learn_lock; |
@@ -1443,6 +1444,7 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, | |||
1443 | struct qlcnic_host_rds_ring *rds_ring, u8 ring_id); | 1444 | struct qlcnic_host_rds_ring *rds_ring, u8 ring_id); |
1444 | int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max); | 1445 | int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max); |
1445 | void qlcnic_set_multi(struct net_device *netdev); | 1446 | void qlcnic_set_multi(struct net_device *netdev); |
1447 | void __qlcnic_set_multi(struct net_device *netdev); | ||
1446 | int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *); | 1448 | int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *); |
1447 | int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); | 1449 | int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); |
1448 | void qlcnic_free_mac_list(struct qlcnic_adapter *adapter); | 1450 | void qlcnic_free_mac_list(struct qlcnic_adapter *adapter); |
@@ -1527,6 +1529,8 @@ void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, | |||
1527 | int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); | 1529 | int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); |
1528 | int qlcnic_read_mac_addr(struct qlcnic_adapter *); | 1530 | int qlcnic_read_mac_addr(struct qlcnic_adapter *); |
1529 | int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); | 1531 | int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); |
1532 | void qlcnic_sriov_vf_schedule_multi(struct net_device *); | ||
1533 | void qlcnic_vf_add_mc_list(struct net_device *); | ||
1530 | 1534 | ||
1531 | /* | 1535 | /* |
1532 | * QLOGIC Board information | 1536 | * QLOGIC Board information |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index f89cc7a3fe6c..ddc130b23378 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | |||
@@ -496,7 +496,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) | |||
496 | return 0; | 496 | return 0; |
497 | } | 497 | } |
498 | 498 | ||
499 | void qlcnic_set_multi(struct net_device *netdev) | 499 | void __qlcnic_set_multi(struct net_device *netdev) |
500 | { | 500 | { |
501 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | 501 | struct qlcnic_adapter *adapter = netdev_priv(netdev); |
502 | struct netdev_hw_addr *ha; | 502 | struct netdev_hw_addr *ha; |
@@ -508,7 +508,8 @@ void qlcnic_set_multi(struct net_device *netdev) | |||
508 | if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) | 508 | if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) |
509 | return; | 509 | return; |
510 | 510 | ||
511 | qlcnic_nic_add_mac(adapter, adapter->mac_addr); | 511 | if (!qlcnic_sriov_vf_check(adapter)) |
512 | qlcnic_nic_add_mac(adapter, adapter->mac_addr); | ||
512 | qlcnic_nic_add_mac(adapter, bcast_addr); | 513 | qlcnic_nic_add_mac(adapter, bcast_addr); |
513 | 514 | ||
514 | if (netdev->flags & IFF_PROMISC) { | 515 | if (netdev->flags & IFF_PROMISC) { |
@@ -523,23 +524,53 @@ void qlcnic_set_multi(struct net_device *netdev) | |||
523 | goto send_fw_cmd; | 524 | goto send_fw_cmd; |
524 | } | 525 | } |
525 | 526 | ||
526 | if (!netdev_mc_empty(netdev)) { | 527 | if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) { |
527 | netdev_for_each_mc_addr(ha, netdev) { | 528 | netdev_for_each_mc_addr(ha, netdev) { |
528 | qlcnic_nic_add_mac(adapter, ha->addr); | 529 | qlcnic_nic_add_mac(adapter, ha->addr); |
529 | } | 530 | } |
530 | } | 531 | } |
531 | 532 | ||
533 | if (qlcnic_sriov_vf_check(adapter)) | ||
534 | qlcnic_vf_add_mc_list(netdev); | ||
535 | |||
532 | send_fw_cmd: | 536 | send_fw_cmd: |
533 | if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) { | 537 | if (!qlcnic_sriov_vf_check(adapter)) { |
534 | qlcnic_alloc_lb_filters_mem(adapter); | 538 | if (mode == VPORT_MISS_MODE_ACCEPT_ALL && |
535 | adapter->drv_mac_learn = true; | 539 | !adapter->fdb_mac_learn) { |
536 | } else { | 540 | qlcnic_alloc_lb_filters_mem(adapter); |
537 | adapter->drv_mac_learn = false; | 541 | adapter->drv_mac_learn = true; |
542 | } else { | ||
543 | adapter->drv_mac_learn = false; | ||
544 | } | ||
538 | } | 545 | } |
539 | 546 | ||
540 | qlcnic_nic_set_promisc(adapter, mode); | 547 | qlcnic_nic_set_promisc(adapter, mode); |
541 | } | 548 | } |
542 | 549 | ||
550 | void qlcnic_set_multi(struct net_device *netdev) | ||
551 | { | ||
552 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
553 | struct netdev_hw_addr *ha; | ||
554 | struct qlcnic_mac_list_s *cur; | ||
555 | |||
556 | if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) | ||
557 | return; | ||
558 | if (qlcnic_sriov_vf_check(adapter)) { | ||
559 | if (!netdev_mc_empty(netdev)) { | ||
560 | netdev_for_each_mc_addr(ha, netdev) { | ||
561 | cur = kzalloc(sizeof(struct qlcnic_mac_list_s), | ||
562 | GFP_ATOMIC); | ||
563 | memcpy(cur->mac_addr, | ||
564 | ha->addr, ETH_ALEN); | ||
565 | list_add_tail(&cur->list, &adapter->vf_mc_list); | ||
566 | } | ||
567 | } | ||
568 | qlcnic_sriov_vf_schedule_multi(adapter->netdev); | ||
569 | return; | ||
570 | } | ||
571 | __qlcnic_set_multi(netdev); | ||
572 | } | ||
573 | |||
543 | int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) | 574 | int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) |
544 | { | 575 | { |
545 | struct qlcnic_nic_req req; | 576 | struct qlcnic_nic_req req; |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 7d5727c80b41..3ee593ee13cf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | |||
@@ -1425,6 +1425,8 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev) | |||
1425 | if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state)) | 1425 | if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state)) |
1426 | return; | 1426 | return; |
1427 | 1427 | ||
1428 | if (qlcnic_sriov_vf_check(adapter)) | ||
1429 | qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc); | ||
1428 | smp_mb(); | 1430 | smp_mb(); |
1429 | spin_lock(&adapter->tx_clean_lock); | 1431 | spin_lock(&adapter->tx_clean_lock); |
1430 | netif_carrier_off(netdev); | 1432 | netif_carrier_off(netdev); |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index 3c05f170801d..b476ebac2439 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | |||
@@ -133,9 +133,17 @@ struct qlcnic_vf_info { | |||
133 | struct qlcnic_vport *vp; | 133 | struct qlcnic_vport *vp; |
134 | }; | 134 | }; |
135 | 135 | ||
136 | struct qlcnic_async_work_list { | ||
137 | struct list_head list; | ||
138 | struct work_struct work; | ||
139 | void *ptr; | ||
140 | }; | ||
141 | |||
136 | struct qlcnic_back_channel { | 142 | struct qlcnic_back_channel { |
137 | u16 trans_counter; | 143 | u16 trans_counter; |
138 | struct workqueue_struct *bc_trans_wq; | 144 | struct workqueue_struct *bc_trans_wq; |
145 | struct workqueue_struct *bc_async_wq; | ||
146 | struct list_head async_list; | ||
139 | }; | 147 | }; |
140 | 148 | ||
141 | struct qlcnic_sriov { | 149 | struct qlcnic_sriov { |
@@ -156,6 +164,7 @@ int qlcnic_sriov_func_to_index(struct qlcnic_adapter *, u8); | |||
156 | int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); | 164 | int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); |
157 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); | 165 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); |
158 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); | 166 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); |
167 | void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *); | ||
159 | 168 | ||
160 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) | 169 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) |
161 | { | 170 | { |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 6e927f2eb3c7..14e9ebd3b73a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | |||
@@ -141,6 +141,16 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | |||
141 | 141 | ||
142 | bc->bc_trans_wq = wq; | 142 | bc->bc_trans_wq = wq; |
143 | 143 | ||
144 | wq = create_singlethread_workqueue("async"); | ||
145 | if (wq == NULL) { | ||
146 | err = -ENOMEM; | ||
147 | dev_err(&adapter->pdev->dev, "Cannot create async workqueue\n"); | ||
148 | goto qlcnic_destroy_trans_wq; | ||
149 | } | ||
150 | |||
151 | bc->bc_async_wq = wq; | ||
152 | INIT_LIST_HEAD(&bc->async_list); | ||
153 | |||
144 | for (i = 0; i < num_vfs; i++) { | 154 | for (i = 0; i < num_vfs; i++) { |
145 | vf = &sriov->vf_info[i]; | 155 | vf = &sriov->vf_info[i]; |
146 | vf->adapter = adapter; | 156 | vf->adapter = adapter; |
@@ -156,7 +166,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | |||
156 | vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); | 166 | vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); |
157 | if (!vp) { | 167 | if (!vp) { |
158 | err = -ENOMEM; | 168 | err = -ENOMEM; |
159 | goto qlcnic_destroy_trans_wq; | 169 | goto qlcnic_destroy_async_wq; |
160 | } | 170 | } |
161 | sriov->vf_info[i].vp = vp; | 171 | sriov->vf_info[i].vp = vp; |
162 | random_ether_addr(vp->mac); | 172 | random_ether_addr(vp->mac); |
@@ -168,6 +178,9 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | |||
168 | 178 | ||
169 | return 0; | 179 | return 0; |
170 | 180 | ||
181 | qlcnic_destroy_async_wq: | ||
182 | destroy_workqueue(bc->bc_async_wq); | ||
183 | |||
171 | qlcnic_destroy_trans_wq: | 184 | qlcnic_destroy_trans_wq: |
172 | destroy_workqueue(bc->bc_trans_wq); | 185 | destroy_workqueue(bc->bc_trans_wq); |
173 | 186 | ||
@@ -188,6 +201,8 @@ void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) | |||
188 | if (!qlcnic_sriov_enable_check(adapter)) | 201 | if (!qlcnic_sriov_enable_check(adapter)) |
189 | return; | 202 | return; |
190 | 203 | ||
204 | qlcnic_sriov_cleanup_async_list(bc); | ||
205 | destroy_workqueue(bc->bc_async_wq); | ||
191 | destroy_workqueue(bc->bc_trans_wq); | 206 | destroy_workqueue(bc->bc_trans_wq); |
192 | 207 | ||
193 | for (i = 0; i < sriov->num_vfs; i++) | 208 | for (i = 0; i < sriov->num_vfs; i++) |
@@ -351,6 +366,7 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, | |||
351 | { | 366 | { |
352 | int err; | 367 | int err; |
353 | 368 | ||
369 | INIT_LIST_HEAD(&adapter->vf_mc_list); | ||
354 | if (!qlcnic_use_msi_x && !!qlcnic_use_msi) | 370 | if (!qlcnic_use_msi_x && !!qlcnic_use_msi) |
355 | dev_warn(&adapter->pdev->dev, | 371 | dev_warn(&adapter->pdev->dev, |
356 | "83xx adapter do not support MSI interrupts\n"); | 372 | "83xx adapter do not support MSI interrupts\n"); |
@@ -1167,3 +1183,115 @@ out: | |||
1167 | qlcnic_free_mbx_args(&cmd); | 1183 | qlcnic_free_mbx_args(&cmd); |
1168 | return ret; | 1184 | return ret; |
1169 | } | 1185 | } |
1186 | |||
1187 | void qlcnic_vf_add_mc_list(struct net_device *netdev) | ||
1188 | { | ||
1189 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1190 | struct qlcnic_mac_list_s *cur; | ||
1191 | struct list_head *head, tmp_list; | ||
1192 | |||
1193 | INIT_LIST_HEAD(&tmp_list); | ||
1194 | head = &adapter->vf_mc_list; | ||
1195 | netif_addr_lock_bh(netdev); | ||
1196 | |||
1197 | while (!list_empty(head)) { | ||
1198 | cur = list_entry(head->next, struct qlcnic_mac_list_s, list); | ||
1199 | list_move(&cur->list, &tmp_list); | ||
1200 | } | ||
1201 | |||
1202 | netif_addr_unlock_bh(netdev); | ||
1203 | |||
1204 | while (!list_empty(&tmp_list)) { | ||
1205 | cur = list_entry((&tmp_list)->next, | ||
1206 | struct qlcnic_mac_list_s, list); | ||
1207 | qlcnic_nic_add_mac(adapter, cur->mac_addr); | ||
1208 | list_del(&cur->list); | ||
1209 | kfree(cur); | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc) | ||
1214 | { | ||
1215 | struct list_head *head = &bc->async_list; | ||
1216 | struct qlcnic_async_work_list *entry; | ||
1217 | |||
1218 | while (!list_empty(head)) { | ||
1219 | entry = list_entry(head->next, struct qlcnic_async_work_list, | ||
1220 | list); | ||
1221 | cancel_work_sync(&entry->work); | ||
1222 | list_del(&entry->list); | ||
1223 | kfree(entry); | ||
1224 | } | ||
1225 | } | ||
1226 | |||
1227 | static void qlcnic_sriov_vf_set_multi(struct net_device *netdev) | ||
1228 | { | ||
1229 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1230 | |||
1231 | if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) | ||
1232 | return; | ||
1233 | |||
1234 | __qlcnic_set_multi(netdev); | ||
1235 | } | ||
1236 | |||
1237 | static void qlcnic_sriov_handle_async_multi(struct work_struct *work) | ||
1238 | { | ||
1239 | struct qlcnic_async_work_list *entry; | ||
1240 | struct net_device *netdev; | ||
1241 | |||
1242 | entry = container_of(work, struct qlcnic_async_work_list, work); | ||
1243 | netdev = (struct net_device *)entry->ptr; | ||
1244 | |||
1245 | qlcnic_sriov_vf_set_multi(netdev); | ||
1246 | return; | ||
1247 | } | ||
1248 | |||
1249 | static struct qlcnic_async_work_list * | ||
1250 | qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc) | ||
1251 | { | ||
1252 | struct list_head *node; | ||
1253 | struct qlcnic_async_work_list *entry = NULL; | ||
1254 | u8 empty = 0; | ||
1255 | |||
1256 | list_for_each(node, &bc->async_list) { | ||
1257 | entry = list_entry(node, struct qlcnic_async_work_list, list); | ||
1258 | if (!work_pending(&entry->work)) { | ||
1259 | empty = 1; | ||
1260 | break; | ||
1261 | } | ||
1262 | } | ||
1263 | |||
1264 | if (!empty) { | ||
1265 | entry = kzalloc(sizeof(struct qlcnic_async_work_list), | ||
1266 | GFP_ATOMIC); | ||
1267 | if (entry == NULL) | ||
1268 | return NULL; | ||
1269 | list_add_tail(&entry->list, &bc->async_list); | ||
1270 | } | ||
1271 | |||
1272 | return entry; | ||
1273 | } | ||
1274 | |||
1275 | static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc, | ||
1276 | work_func_t func, void *data) | ||
1277 | { | ||
1278 | struct qlcnic_async_work_list *entry = NULL; | ||
1279 | |||
1280 | entry = qlcnic_sriov_get_free_node_async_work(bc); | ||
1281 | if (!entry) | ||
1282 | return; | ||
1283 | |||
1284 | entry->ptr = data; | ||
1285 | INIT_WORK(&entry->work, func); | ||
1286 | queue_work(bc->bc_async_wq, &entry->work); | ||
1287 | } | ||
1288 | |||
1289 | void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev) | ||
1290 | { | ||
1291 | |||
1292 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1293 | struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc; | ||
1294 | |||
1295 | qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi, | ||
1296 | netdev); | ||
1297 | } | ||