diff options
author | Scott Feldman <scofeldm@cisco.com> | 2009-12-23 08:27:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-24 00:03:39 -0500 |
commit | b3d18d191bb805f3effdfc083c4ce79789470b46 (patch) | |
tree | 034a2d4cfda07b9f843942bc33a3e43ea2572489 | |
parent | fe45332ed289d91e57eca11bfd1ca75d6e420ab4 (diff) |
enic: Bug fix: use safe queue shutdown in dev->stop
Fix dev->stop shutdown bug where driver was stopping xmit queue and then
disabling intrs. Fix is to disable intrs first and then stop the xmit
queue, otherwise an interrupt could cause the queue to be rewoken. Also,
no need to explicitly do queue servicing because queues are cleaned and
reset back to initial state at end of dev->stop. Servicing queues also
had the side-effect of also rewakening the xmit queue, which is not what
we want.
Signed-off-by: Vasanthy Kolluri <vkolluri@cisco.com>
Signed-off-by: Scott Feldman <scofeldm@cisco.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/enic/enic.h | 2 | ||||
-rw-r--r-- | drivers/net/enic/enic_main.c | 62 | ||||
-rw-r--r-- | drivers/net/enic/vnic_intr.h | 1 |
3 files changed, 27 insertions, 38 deletions
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h index e1c2076228ba..8dd0105a8d9c 100644 --- a/drivers/net/enic/enic.h +++ b/drivers/net/enic/enic.h | |||
@@ -34,7 +34,7 @@ | |||
34 | 34 | ||
35 | #define DRV_NAME "enic" | 35 | #define DRV_NAME "enic" |
36 | #define DRV_DESCRIPTION "Cisco 10G Ethernet Driver" | 36 | #define DRV_DESCRIPTION "Cisco 10G Ethernet Driver" |
37 | #define DRV_VERSION "1.1.0.100" | 37 | #define DRV_VERSION "1.1.0.241a" |
38 | #define DRV_COPYRIGHT "Copyright 2008-2009 Cisco Systems, Inc" | 38 | #define DRV_COPYRIGHT "Copyright 2008-2009 Cisco Systems, Inc" |
39 | #define PFX DRV_NAME ": " | 39 | #define PFX DRV_NAME ": " |
40 | 40 | ||
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index f875751af15e..b4a11befb3b3 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c | |||
@@ -1084,34 +1084,6 @@ static int enic_rq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, | |||
1084 | return 0; | 1084 | return 0; |
1085 | } | 1085 | } |
1086 | 1086 | ||
1087 | static void enic_rq_drop_buf(struct vnic_rq *rq, | ||
1088 | struct cq_desc *cq_desc, struct vnic_rq_buf *buf, | ||
1089 | int skipped, void *opaque) | ||
1090 | { | ||
1091 | struct enic *enic = vnic_dev_priv(rq->vdev); | ||
1092 | struct sk_buff *skb = buf->os_buf; | ||
1093 | |||
1094 | if (skipped) | ||
1095 | return; | ||
1096 | |||
1097 | pci_unmap_single(enic->pdev, buf->dma_addr, | ||
1098 | buf->len, PCI_DMA_FROMDEVICE); | ||
1099 | |||
1100 | dev_kfree_skb_any(skb); | ||
1101 | } | ||
1102 | |||
1103 | static int enic_rq_service_drop(struct vnic_dev *vdev, struct cq_desc *cq_desc, | ||
1104 | u8 type, u16 q_number, u16 completed_index, void *opaque) | ||
1105 | { | ||
1106 | struct enic *enic = vnic_dev_priv(vdev); | ||
1107 | |||
1108 | vnic_rq_service(&enic->rq[q_number], cq_desc, | ||
1109 | completed_index, VNIC_RQ_RETURN_DESC, | ||
1110 | enic_rq_drop_buf, opaque); | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | static int enic_poll(struct napi_struct *napi, int budget) | 1087 | static int enic_poll(struct napi_struct *napi, int budget) |
1116 | { | 1088 | { |
1117 | struct enic *enic = container_of(napi, struct enic, napi); | 1089 | struct enic *enic = container_of(napi, struct enic, napi); |
@@ -1304,6 +1276,24 @@ static int enic_request_intr(struct enic *enic) | |||
1304 | return err; | 1276 | return err; |
1305 | } | 1277 | } |
1306 | 1278 | ||
1279 | static void enic_synchronize_irqs(struct enic *enic) | ||
1280 | { | ||
1281 | unsigned int i; | ||
1282 | |||
1283 | switch (vnic_dev_get_intr_mode(enic->vdev)) { | ||
1284 | case VNIC_DEV_INTR_MODE_INTX: | ||
1285 | case VNIC_DEV_INTR_MODE_MSI: | ||
1286 | synchronize_irq(enic->pdev->irq); | ||
1287 | break; | ||
1288 | case VNIC_DEV_INTR_MODE_MSIX: | ||
1289 | for (i = 0; i < enic->intr_count; i++) | ||
1290 | synchronize_irq(enic->msix_entry[i].vector); | ||
1291 | break; | ||
1292 | default: | ||
1293 | break; | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1307 | static int enic_notify_set(struct enic *enic) | 1297 | static int enic_notify_set(struct enic *enic) |
1308 | { | 1298 | { |
1309 | int err; | 1299 | int err; |
@@ -1409,16 +1399,19 @@ static int enic_stop(struct net_device *netdev) | |||
1409 | unsigned int i; | 1399 | unsigned int i; |
1410 | int err; | 1400 | int err; |
1411 | 1401 | ||
1402 | for (i = 0; i < enic->intr_count; i++) | ||
1403 | vnic_intr_mask(&enic->intr[i]); | ||
1404 | |||
1405 | enic_synchronize_irqs(enic); | ||
1406 | |||
1412 | del_timer_sync(&enic->notify_timer); | 1407 | del_timer_sync(&enic->notify_timer); |
1413 | 1408 | ||
1414 | spin_lock(&enic->devcmd_lock); | 1409 | spin_lock(&enic->devcmd_lock); |
1415 | vnic_dev_disable(enic->vdev); | 1410 | vnic_dev_disable(enic->vdev); |
1416 | spin_unlock(&enic->devcmd_lock); | 1411 | spin_unlock(&enic->devcmd_lock); |
1417 | napi_disable(&enic->napi); | 1412 | napi_disable(&enic->napi); |
1418 | netif_stop_queue(netdev); | 1413 | netif_carrier_off(netdev); |
1419 | 1414 | netif_tx_disable(netdev); | |
1420 | for (i = 0; i < enic->intr_count; i++) | ||
1421 | vnic_intr_mask(&enic->intr[i]); | ||
1422 | 1415 | ||
1423 | for (i = 0; i < enic->wq_count; i++) { | 1416 | for (i = 0; i < enic->wq_count; i++) { |
1424 | err = vnic_wq_disable(&enic->wq[i]); | 1417 | err = vnic_wq_disable(&enic->wq[i]); |
@@ -1436,11 +1429,6 @@ static int enic_stop(struct net_device *netdev) | |||
1436 | spin_unlock(&enic->devcmd_lock); | 1429 | spin_unlock(&enic->devcmd_lock); |
1437 | enic_free_intr(enic); | 1430 | enic_free_intr(enic); |
1438 | 1431 | ||
1439 | (void)vnic_cq_service(&enic->cq[ENIC_CQ_RQ], | ||
1440 | -1, enic_rq_service_drop, NULL); | ||
1441 | (void)vnic_cq_service(&enic->cq[ENIC_CQ_WQ], | ||
1442 | -1, enic_wq_service, NULL); | ||
1443 | |||
1444 | for (i = 0; i < enic->wq_count; i++) | 1432 | for (i = 0; i < enic->wq_count; i++) |
1445 | vnic_wq_clean(&enic->wq[i], enic_free_wq_buf); | 1433 | vnic_wq_clean(&enic->wq[i], enic_free_wq_buf); |
1446 | for (i = 0; i < enic->rq_count; i++) | 1434 | for (i = 0; i < enic->rq_count; i++) |
diff --git a/drivers/net/enic/vnic_intr.h b/drivers/net/enic/vnic_intr.h index 9a53604edce6..f79a722b6a03 100644 --- a/drivers/net/enic/vnic_intr.h +++ b/drivers/net/enic/vnic_intr.h | |||
@@ -61,6 +61,7 @@ static inline void vnic_intr_unmask(struct vnic_intr *intr) | |||
61 | static inline void vnic_intr_mask(struct vnic_intr *intr) | 61 | static inline void vnic_intr_mask(struct vnic_intr *intr) |
62 | { | 62 | { |
63 | iowrite32(1, &intr->ctrl->mask); | 63 | iowrite32(1, &intr->ctrl->mask); |
64 | (void)ioread32(&intr->ctrl->mask); | ||
64 | } | 65 | } |
65 | 66 | ||
66 | static inline void vnic_intr_return_credits(struct vnic_intr *intr, | 67 | static inline void vnic_intr_return_credits(struct vnic_intr *intr, |