diff options
author | Sathya Perla <sathyap@serverengines.com> | 2010-03-02 06:56:39 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-02 06:56:39 -0500 |
commit | f31e50a802baae939c49819b8acd8f077019d398 (patch) | |
tree | f9bb9e3950933db922a4ce814d7ac281569778f1 /drivers/net/benet | |
parent | b88aafd365bc6a2222e2d03ff320adea3a37f628 (diff) |
be2net: fix tx completion polling
In tx/mcc polling, napi_complete() is being incorrectly called
before reaping tx completions. This can cause tx compl processing
to be scheduled on another cpu concurrently which can result in a panic.
This if fixed by calling napi complete() after tx/mcc compl processing
but before re-enabling interrupts (via a cq notify).
Signed-off-by: Sathya Perla <sathyap@serverengines.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/benet')
-rw-r--r-- | drivers/net/benet/be_cmds.c | 26 | ||||
-rw-r--r-- | drivers/net/benet/be_cmds.h | 2 | ||||
-rw-r--r-- | drivers/net/benet/be_main.c | 47 |
3 files changed, 37 insertions, 38 deletions
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index c8a2bacb1d13..4b1f80519ca4 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c | |||
@@ -119,10 +119,10 @@ void be_async_mcc_disable(struct be_adapter *adapter) | |||
119 | adapter->mcc_obj.rearm_cq = false; | 119 | adapter->mcc_obj.rearm_cq = false; |
120 | } | 120 | } |
121 | 121 | ||
122 | int be_process_mcc(struct be_adapter *adapter) | 122 | int be_process_mcc(struct be_adapter *adapter, int *status) |
123 | { | 123 | { |
124 | struct be_mcc_compl *compl; | 124 | struct be_mcc_compl *compl; |
125 | int num = 0, status = 0; | 125 | int num = 0; |
126 | struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; | 126 | struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; |
127 | 127 | ||
128 | spin_lock_bh(&adapter->mcc_cq_lock); | 128 | spin_lock_bh(&adapter->mcc_cq_lock); |
@@ -135,31 +135,31 @@ int be_process_mcc(struct be_adapter *adapter) | |||
135 | be_async_link_state_process(adapter, | 135 | be_async_link_state_process(adapter, |
136 | (struct be_async_event_link_state *) compl); | 136 | (struct be_async_event_link_state *) compl); |
137 | } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) { | 137 | } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) { |
138 | status = be_mcc_compl_process(adapter, compl); | 138 | *status = be_mcc_compl_process(adapter, compl); |
139 | atomic_dec(&mcc_obj->q.used); | 139 | atomic_dec(&mcc_obj->q.used); |
140 | } | 140 | } |
141 | be_mcc_compl_use(compl); | 141 | be_mcc_compl_use(compl); |
142 | num++; | 142 | num++; |
143 | } | 143 | } |
144 | 144 | ||
145 | if (num) | ||
146 | be_cq_notify(adapter, mcc_obj->cq.id, mcc_obj->rearm_cq, num); | ||
147 | |||
148 | spin_unlock_bh(&adapter->mcc_cq_lock); | 145 | spin_unlock_bh(&adapter->mcc_cq_lock); |
149 | return status; | 146 | return num; |
150 | } | 147 | } |
151 | 148 | ||
152 | /* Wait till no more pending mcc requests are present */ | 149 | /* Wait till no more pending mcc requests are present */ |
153 | static int be_mcc_wait_compl(struct be_adapter *adapter) | 150 | static int be_mcc_wait_compl(struct be_adapter *adapter) |
154 | { | 151 | { |
155 | #define mcc_timeout 120000 /* 12s timeout */ | 152 | #define mcc_timeout 120000 /* 12s timeout */ |
156 | int i, status; | 153 | int i, num, status = 0; |
154 | struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; | ||
155 | |||
157 | for (i = 0; i < mcc_timeout; i++) { | 156 | for (i = 0; i < mcc_timeout; i++) { |
158 | status = be_process_mcc(adapter); | 157 | num = be_process_mcc(adapter, &status); |
159 | if (status) | 158 | if (num) |
160 | return status; | 159 | be_cq_notify(adapter, mcc_obj->cq.id, |
160 | mcc_obj->rearm_cq, num); | ||
161 | 161 | ||
162 | if (atomic_read(&adapter->mcc_obj.q.used) == 0) | 162 | if (atomic_read(&mcc_obj->q.used) == 0) |
163 | break; | 163 | break; |
164 | udelay(100); | 164 | udelay(100); |
165 | } | 165 | } |
@@ -167,7 +167,7 @@ static int be_mcc_wait_compl(struct be_adapter *adapter) | |||
167 | dev_err(&adapter->pdev->dev, "mccq poll timed out\n"); | 167 | dev_err(&adapter->pdev->dev, "mccq poll timed out\n"); |
168 | return -1; | 168 | return -1; |
169 | } | 169 | } |
170 | return 0; | 170 | return status; |
171 | } | 171 | } |
172 | 172 | ||
173 | /* Notify MCC requests and wait for completion */ | 173 | /* Notify MCC requests and wait for completion */ |
diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index 728b0d736929..cce61f9a3714 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h | |||
@@ -920,7 +920,7 @@ extern int be_cmd_get_flow_control(struct be_adapter *adapter, | |||
920 | extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, | 920 | extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, |
921 | u32 *port_num, u32 *cap); | 921 | u32 *port_num, u32 *cap); |
922 | extern int be_cmd_reset_function(struct be_adapter *adapter); | 922 | extern int be_cmd_reset_function(struct be_adapter *adapter); |
923 | extern int be_process_mcc(struct be_adapter *adapter); | 923 | extern int be_process_mcc(struct be_adapter *adapter, int *status); |
924 | extern int be_cmd_set_beacon_state(struct be_adapter *adapter, | 924 | extern int be_cmd_set_beacon_state(struct be_adapter *adapter, |
925 | u8 port_num, u8 beacon, u8 status, u8 state); | 925 | u8 port_num, u8 beacon, u8 status, u8 state); |
926 | extern int be_cmd_get_beacon_state(struct be_adapter *adapter, | 926 | extern int be_cmd_get_beacon_state(struct be_adapter *adapter, |
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 27ccdd80257b..a703ed8e24fe 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c | |||
@@ -583,7 +583,7 @@ static void be_set_multicast_list(struct net_device *netdev) | |||
583 | } | 583 | } |
584 | 584 | ||
585 | be_cmd_multicast_set(adapter, adapter->if_handle, netdev, | 585 | be_cmd_multicast_set(adapter, adapter->if_handle, netdev, |
586 | &adapter->mc_cmd_mem); | 586 | &adapter->mc_cmd_mem); |
587 | done: | 587 | done: |
588 | return; | 588 | return; |
589 | } | 589 | } |
@@ -1469,23 +1469,38 @@ int be_poll_rx(struct napi_struct *napi, int budget) | |||
1469 | return work_done; | 1469 | return work_done; |
1470 | } | 1470 | } |
1471 | 1471 | ||
1472 | void be_process_tx(struct be_adapter *adapter) | 1472 | /* As TX and MCC share the same EQ check for both TX and MCC completions. |
1473 | * For TX/MCC we don't honour budget; consume everything | ||
1474 | */ | ||
1475 | static int be_poll_tx_mcc(struct napi_struct *napi, int budget) | ||
1473 | { | 1476 | { |
1477 | struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); | ||
1478 | struct be_adapter *adapter = | ||
1479 | container_of(tx_eq, struct be_adapter, tx_eq); | ||
1474 | struct be_queue_info *txq = &adapter->tx_obj.q; | 1480 | struct be_queue_info *txq = &adapter->tx_obj.q; |
1475 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; | 1481 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; |
1476 | struct be_eth_tx_compl *txcp; | 1482 | struct be_eth_tx_compl *txcp; |
1477 | u32 num_cmpl = 0; | 1483 | int tx_compl = 0, mcc_compl, status = 0; |
1478 | u16 end_idx; | 1484 | u16 end_idx; |
1479 | 1485 | ||
1480 | while ((txcp = be_tx_compl_get(tx_cq))) { | 1486 | while ((txcp = be_tx_compl_get(tx_cq))) { |
1481 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, | 1487 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, |
1482 | wrb_index, txcp); | 1488 | wrb_index, txcp); |
1483 | be_tx_compl_process(adapter, end_idx); | 1489 | be_tx_compl_process(adapter, end_idx); |
1484 | num_cmpl++; | 1490 | tx_compl++; |
1485 | } | 1491 | } |
1486 | 1492 | ||
1487 | if (num_cmpl) { | 1493 | mcc_compl = be_process_mcc(adapter, &status); |
1488 | be_cq_notify(adapter, tx_cq->id, true, num_cmpl); | 1494 | |
1495 | napi_complete(napi); | ||
1496 | |||
1497 | if (mcc_compl) { | ||
1498 | struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; | ||
1499 | be_cq_notify(adapter, mcc_obj->cq.id, true, mcc_compl); | ||
1500 | } | ||
1501 | |||
1502 | if (tx_compl) { | ||
1503 | be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl); | ||
1489 | 1504 | ||
1490 | /* As Tx wrbs have been freed up, wake up netdev queue if | 1505 | /* As Tx wrbs have been freed up, wake up netdev queue if |
1491 | * it was stopped due to lack of tx wrbs. | 1506 | * it was stopped due to lack of tx wrbs. |
@@ -1496,24 +1511,8 @@ void be_process_tx(struct be_adapter *adapter) | |||
1496 | } | 1511 | } |
1497 | 1512 | ||
1498 | drvr_stats(adapter)->be_tx_events++; | 1513 | drvr_stats(adapter)->be_tx_events++; |
1499 | drvr_stats(adapter)->be_tx_compl += num_cmpl; | 1514 | drvr_stats(adapter)->be_tx_compl += tx_compl; |
1500 | } | 1515 | } |
1501 | } | ||
1502 | |||
1503 | /* As TX and MCC share the same EQ check for both TX and MCC completions. | ||
1504 | * For TX/MCC we don't honour budget; consume everything | ||
1505 | */ | ||
1506 | static int be_poll_tx_mcc(struct napi_struct *napi, int budget) | ||
1507 | { | ||
1508 | struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); | ||
1509 | struct be_adapter *adapter = | ||
1510 | container_of(tx_eq, struct be_adapter, tx_eq); | ||
1511 | |||
1512 | napi_complete(napi); | ||
1513 | |||
1514 | be_process_tx(adapter); | ||
1515 | |||
1516 | be_process_mcc(adapter); | ||
1517 | 1516 | ||
1518 | return 1; | 1517 | return 1; |
1519 | } | 1518 | } |