aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/qlogic/qlcnic
diff options
context:
space:
mode:
authorRajesh Borundia <rajesh.borundia@qlogic.com>2013-03-29 01:46:38 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-29 15:51:06 -0400
commite8b508ef71fb70ec761086532716b19d3c4773e5 (patch)
tree22e27a6c8369ffff72375ead0a120bbf16b9f9a0 /drivers/net/ethernet/qlogic/qlcnic
parent7cb03b2347d5edace4fb8e7dd9d6c3889368a179 (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.h4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c47
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h9
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c130
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);
1444int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max); 1445int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);
1445void qlcnic_set_multi(struct net_device *netdev); 1446void qlcnic_set_multi(struct net_device *netdev);
1447void __qlcnic_set_multi(struct net_device *netdev);
1446int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *); 1448int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *);
1447int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); 1449int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
1448void qlcnic_free_mac_list(struct qlcnic_adapter *adapter); 1450void qlcnic_free_mac_list(struct qlcnic_adapter *adapter);
@@ -1527,6 +1529,8 @@ void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int,
1527int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); 1529int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
1528int qlcnic_read_mac_addr(struct qlcnic_adapter *); 1530int qlcnic_read_mac_addr(struct qlcnic_adapter *);
1529int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); 1531int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
1532void qlcnic_sriov_vf_schedule_multi(struct net_device *);
1533void 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
499void qlcnic_set_multi(struct net_device *netdev) 499void __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
532send_fw_cmd: 536send_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
550void 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
543int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) 574int 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
136struct qlcnic_async_work_list {
137 struct list_head list;
138 struct work_struct work;
139 void *ptr;
140};
141
136struct qlcnic_back_channel { 142struct 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
141struct qlcnic_sriov { 149struct qlcnic_sriov {
@@ -156,6 +164,7 @@ int qlcnic_sriov_func_to_index(struct qlcnic_adapter *, u8);
156int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); 164int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8);
157void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); 165void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32);
158int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); 166int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8);
167void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *);
159 168
160static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) 169static 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
181qlcnic_destroy_async_wq:
182 destroy_workqueue(bc->bc_async_wq);
183
171qlcnic_destroy_trans_wq: 184qlcnic_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
1187void 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
1213void 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
1227static 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
1237static 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
1249static struct qlcnic_async_work_list *
1250qlcnic_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
1275static 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
1289void 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}