Skip to content

Commit e346e23

Browse files
finist0davem330
authored andcommitted
qed: Fix scheduling in a tasklet while getting stats
Here we've got to a situation when tasklet called usleep_range() in PTT acquire logic, thus welcome to the "scheduling while atomic" BUG(). BUG: scheduling while atomic: swapper/24/0/0x00000100 [<ffffffffb41c6199>] schedule+0x29/0x70 [<ffffffffb41c5512>] schedule_hrtimeout_range_clock+0xb2/0x150 [<ffffffffb41c55c3>] schedule_hrtimeout_range+0x13/0x20 [<ffffffffb41c3bcf>] usleep_range+0x4f/0x70 [<ffffffffc08d3e58>] qed_ptt_acquire+0x38/0x100 [qed] [<ffffffffc08eac48>] _qed_get_vport_stats+0x458/0x580 [qed] [<ffffffffc08ead8c>] qed_get_vport_stats+0x1c/0xd0 [qed] [<ffffffffc08dffd3>] qed_get_protocol_stats+0x93/0x100 [qed] qed_mcp_send_protocol_stats case MFW_DRV_MSG_GET_LAN_STATS: case MFW_DRV_MSG_GET_FCOE_STATS: case MFW_DRV_MSG_GET_ISCSI_STATS: case MFW_DRV_MSG_GET_RDMA_STATS: [<ffffffffc08e36d8>] qed_mcp_handle_events+0x2d8/0x890 [qed] qed_int_assertion qed_int_attentions [<ffffffffc08d9490>] qed_int_sp_dpc+0xa50/0xdc0 [qed] [<ffffffffb3aa7623>] tasklet_action+0x83/0x140 [<ffffffffb41d9125>] __do_softirq+0x125/0x2bb [<ffffffffb41d560c>] call_softirq+0x1c/0x30 [<ffffffffb3a30645>] do_softirq+0x65/0xa0 [<ffffffffb3aa78d5>] irq_exit+0x105/0x110 [<ffffffffb41d8996>] do_IRQ+0x56/0xf0 Fix this by making caller to provide the context whether it could be in atomic context flow or not when getting stats from QED driver. QED driver based on the context provided decide to schedule out or not when acquiring the PTT BAR window. We faced the BUG_ON() while getting vport stats, but according to the code same issue could happen for fcoe and iscsi statistics as well, so fixing them too. Fixes: 6c75424 ("qed: Add support for NCSI statistics.") Fixes: 1e128c8 ("qed: Add support for hardware offloaded FCoE.") Fixes: 2f2b261 ("qed: Provide iSCSI statistics to management") Cc: Sudarsana Kalluru <[email protected]> Cc: David Miller <[email protected]> Cc: Manish Chopra <[email protected]> Signed-off-by: Konstantin Khorenko <[email protected]> Reviewed-by: Simon Horman <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8d7ae22 commit e346e23

File tree

9 files changed

+128
-26
lines changed

9 files changed

+128
-26
lines changed

drivers/net/ethernet/qlogic/qed/qed_dev_api.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ void qed_hw_remove(struct qed_dev *cdev);
193193
*/
194194
struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn);
195195

196+
/**
197+
* qed_ptt_acquire_context(): Allocate a PTT window honoring the context
198+
* atomicy.
199+
*
200+
* @p_hwfn: HW device data.
201+
* @is_atomic: Hint from the caller - if the func can sleep or not.
202+
*
203+
* Context: The function should not sleep in case is_atomic == true.
204+
* Return: struct qed_ptt.
205+
*
206+
* Should be called at the entry point to the driver
207+
* (at the beginning of an exported function).
208+
*/
209+
struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn,
210+
bool is_atomic);
211+
196212
/**
197213
* qed_ptt_release(): Release PTT Window.
198214
*

drivers/net/ethernet/qlogic/qed/qed_fcoe.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -693,13 +693,14 @@ static void _qed_fcoe_get_pstats(struct qed_hwfn *p_hwfn,
693693
}
694694

695695
static int qed_fcoe_get_stats(struct qed_hwfn *p_hwfn,
696-
struct qed_fcoe_stats *p_stats)
696+
struct qed_fcoe_stats *p_stats,
697+
bool is_atomic)
697698
{
698699
struct qed_ptt *p_ptt;
699700

700701
memset(p_stats, 0, sizeof(*p_stats));
701702

702-
p_ptt = qed_ptt_acquire(p_hwfn);
703+
p_ptt = qed_ptt_acquire_context(p_hwfn, is_atomic);
703704

704705
if (!p_ptt) {
705706
DP_ERR(p_hwfn, "Failed to acquire ptt\n");
@@ -973,19 +974,27 @@ static int qed_fcoe_destroy_conn(struct qed_dev *cdev,
973974
QED_SPQ_MODE_EBLOCK, NULL);
974975
}
975976

977+
static int qed_fcoe_stats_context(struct qed_dev *cdev,
978+
struct qed_fcoe_stats *stats,
979+
bool is_atomic)
980+
{
981+
return qed_fcoe_get_stats(QED_AFFIN_HWFN(cdev), stats, is_atomic);
982+
}
983+
976984
static int qed_fcoe_stats(struct qed_dev *cdev, struct qed_fcoe_stats *stats)
977985
{
978-
return qed_fcoe_get_stats(QED_AFFIN_HWFN(cdev), stats);
986+
return qed_fcoe_stats_context(cdev, stats, false);
979987
}
980988

981989
void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
982-
struct qed_mcp_fcoe_stats *stats)
990+
struct qed_mcp_fcoe_stats *stats,
991+
bool is_atomic)
983992
{
984993
struct qed_fcoe_stats proto_stats;
985994

986995
/* Retrieve FW statistics */
987996
memset(&proto_stats, 0, sizeof(proto_stats));
988-
if (qed_fcoe_stats(cdev, &proto_stats)) {
997+
if (qed_fcoe_stats_context(cdev, &proto_stats, is_atomic)) {
989998
DP_VERBOSE(cdev, QED_MSG_STORAGE,
990999
"Failed to collect FCoE statistics\n");
9911000
return;

drivers/net/ethernet/qlogic/qed/qed_fcoe.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,20 @@ int qed_fcoe_alloc(struct qed_hwfn *p_hwfn);
2828
void qed_fcoe_setup(struct qed_hwfn *p_hwfn);
2929

3030
void qed_fcoe_free(struct qed_hwfn *p_hwfn);
31+
/**
32+
* qed_get_protocol_stats_fcoe(): Fills provided statistics
33+
* struct with statistics.
34+
*
35+
* @cdev: Qed dev pointer.
36+
* @stats: Points to struct that will be filled with statistics.
37+
* @is_atomic: Hint from the caller - if the func can sleep or not.
38+
*
39+
* Context: The function should not sleep in case is_atomic == true.
40+
* Return: Void.
41+
*/
3142
void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
32-
struct qed_mcp_fcoe_stats *stats);
43+
struct qed_mcp_fcoe_stats *stats,
44+
bool is_atomic);
3345
#else /* CONFIG_QED_FCOE */
3446
static inline int qed_fcoe_alloc(struct qed_hwfn *p_hwfn)
3547
{
@@ -40,7 +52,8 @@ static inline void qed_fcoe_setup(struct qed_hwfn *p_hwfn) {}
4052
static inline void qed_fcoe_free(struct qed_hwfn *p_hwfn) {}
4153

4254
static inline void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
43-
struct qed_mcp_fcoe_stats *stats)
55+
struct qed_mcp_fcoe_stats *stats,
56+
bool is_atomic)
4457
{
4558
}
4659
#endif /* CONFIG_QED_FCOE */

drivers/net/ethernet/qlogic/qed/qed_hw.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
#include "qed_reg_addr.h"
2424
#include "qed_sriov.h"
2525

26-
#define QED_BAR_ACQUIRE_TIMEOUT 1000
26+
#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT 1000
27+
#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP 1000
28+
#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT 100000
29+
#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY 10
2730

2831
/* Invalid values */
2932
#define QED_BAR_INVALID_OFFSET (cpu_to_le32(-1))
@@ -84,12 +87,22 @@ void qed_ptt_pool_free(struct qed_hwfn *p_hwfn)
8487
}
8588

8689
struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn)
90+
{
91+
return qed_ptt_acquire_context(p_hwfn, false);
92+
}
93+
94+
struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn, bool is_atomic)
8795
{
8896
struct qed_ptt *p_ptt;
89-
unsigned int i;
97+
unsigned int i, count;
98+
99+
if (is_atomic)
100+
count = QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT;
101+
else
102+
count = QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT;
90103

91104
/* Take the free PTT from the list */
92-
for (i = 0; i < QED_BAR_ACQUIRE_TIMEOUT; i++) {
105+
for (i = 0; i < count; i++) {
93106
spin_lock_bh(&p_hwfn->p_ptt_pool->lock);
94107

95108
if (!list_empty(&p_hwfn->p_ptt_pool->free_list)) {
@@ -105,7 +118,12 @@ struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn)
105118
}
106119

107120
spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
108-
usleep_range(1000, 2000);
121+
122+
if (is_atomic)
123+
udelay(QED_BAR_ACQUIRE_TIMEOUT_UDELAY);
124+
else
125+
usleep_range(QED_BAR_ACQUIRE_TIMEOUT_USLEEP,
126+
QED_BAR_ACQUIRE_TIMEOUT_USLEEP * 2);
109127
}
110128

111129
DP_NOTICE(p_hwfn, "PTT acquire timeout - failed to allocate PTT\n");

drivers/net/ethernet/qlogic/qed/qed_iscsi.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -999,13 +999,14 @@ static void _qed_iscsi_get_pstats(struct qed_hwfn *p_hwfn,
999999
}
10001000

10011001
static int qed_iscsi_get_stats(struct qed_hwfn *p_hwfn,
1002-
struct qed_iscsi_stats *stats)
1002+
struct qed_iscsi_stats *stats,
1003+
bool is_atomic)
10031004
{
10041005
struct qed_ptt *p_ptt;
10051006

10061007
memset(stats, 0, sizeof(*stats));
10071008

1008-
p_ptt = qed_ptt_acquire(p_hwfn);
1009+
p_ptt = qed_ptt_acquire_context(p_hwfn, is_atomic);
10091010
if (!p_ptt) {
10101011
DP_ERR(p_hwfn, "Failed to acquire ptt\n");
10111012
return -EAGAIN;
@@ -1336,9 +1337,16 @@ static int qed_iscsi_destroy_conn(struct qed_dev *cdev,
13361337
QED_SPQ_MODE_EBLOCK, NULL);
13371338
}
13381339

1340+
static int qed_iscsi_stats_context(struct qed_dev *cdev,
1341+
struct qed_iscsi_stats *stats,
1342+
bool is_atomic)
1343+
{
1344+
return qed_iscsi_get_stats(QED_AFFIN_HWFN(cdev), stats, is_atomic);
1345+
}
1346+
13391347
static int qed_iscsi_stats(struct qed_dev *cdev, struct qed_iscsi_stats *stats)
13401348
{
1341-
return qed_iscsi_get_stats(QED_AFFIN_HWFN(cdev), stats);
1349+
return qed_iscsi_stats_context(cdev, stats, false);
13421350
}
13431351

13441352
static int qed_iscsi_change_mac(struct qed_dev *cdev,
@@ -1358,13 +1366,14 @@ static int qed_iscsi_change_mac(struct qed_dev *cdev,
13581366
}
13591367

13601368
void qed_get_protocol_stats_iscsi(struct qed_dev *cdev,
1361-
struct qed_mcp_iscsi_stats *stats)
1369+
struct qed_mcp_iscsi_stats *stats,
1370+
bool is_atomic)
13621371
{
13631372
struct qed_iscsi_stats proto_stats;
13641373

13651374
/* Retrieve FW statistics */
13661375
memset(&proto_stats, 0, sizeof(proto_stats));
1367-
if (qed_iscsi_stats(cdev, &proto_stats)) {
1376+
if (qed_iscsi_stats_context(cdev, &proto_stats, is_atomic)) {
13681377
DP_VERBOSE(cdev, QED_MSG_STORAGE,
13691378
"Failed to collect ISCSI statistics\n");
13701379
return;

drivers/net/ethernet/qlogic/qed/qed_iscsi.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ void qed_iscsi_free(struct qed_hwfn *p_hwfn);
3939
*
4040
* @cdev: Qed dev pointer.
4141
* @stats: Points to struct that will be filled with statistics.
42+
* @is_atomic: Hint from the caller - if the func can sleep or not.
4243
*
44+
* Context: The function should not sleep in case is_atomic == true.
4345
* Return: Void.
4446
*/
4547
void qed_get_protocol_stats_iscsi(struct qed_dev *cdev,
46-
struct qed_mcp_iscsi_stats *stats);
48+
struct qed_mcp_iscsi_stats *stats,
49+
bool is_atomic);
4750
#else /* IS_ENABLED(CONFIG_QED_ISCSI) */
4851
static inline int qed_iscsi_alloc(struct qed_hwfn *p_hwfn)
4952
{
@@ -56,7 +59,8 @@ static inline void qed_iscsi_free(struct qed_hwfn *p_hwfn) {}
5659

5760
static inline void
5861
qed_get_protocol_stats_iscsi(struct qed_dev *cdev,
59-
struct qed_mcp_iscsi_stats *stats) {}
62+
struct qed_mcp_iscsi_stats *stats,
63+
bool is_atomic) {}
6064
#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */
6165

6266
#endif

drivers/net/ethernet/qlogic/qed/qed_l2.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,8 @@ static void __qed_get_vport_stats(struct qed_hwfn *p_hwfn,
18631863
}
18641864

18651865
static void _qed_get_vport_stats(struct qed_dev *cdev,
1866-
struct qed_eth_stats *stats)
1866+
struct qed_eth_stats *stats,
1867+
bool is_atomic)
18671868
{
18681869
u8 fw_vport = 0;
18691870
int i;
@@ -1872,10 +1873,11 @@ static void _qed_get_vport_stats(struct qed_dev *cdev,
18721873

18731874
for_each_hwfn(cdev, i) {
18741875
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
1875-
struct qed_ptt *p_ptt = IS_PF(cdev) ? qed_ptt_acquire(p_hwfn)
1876-
: NULL;
1876+
struct qed_ptt *p_ptt;
18771877
bool b_get_port_stats;
18781878

1879+
p_ptt = IS_PF(cdev) ? qed_ptt_acquire_context(p_hwfn, is_atomic)
1880+
: NULL;
18791881
if (IS_PF(cdev)) {
18801882
/* The main vport index is relative first */
18811883
if (qed_fw_vport(p_hwfn, 0, &fw_vport)) {
@@ -1900,6 +1902,13 @@ static void _qed_get_vport_stats(struct qed_dev *cdev,
19001902
}
19011903

19021904
void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats)
1905+
{
1906+
qed_get_vport_stats_context(cdev, stats, false);
1907+
}
1908+
1909+
void qed_get_vport_stats_context(struct qed_dev *cdev,
1910+
struct qed_eth_stats *stats,
1911+
bool is_atomic)
19031912
{
19041913
u32 i;
19051914

@@ -1908,7 +1917,7 @@ void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats)
19081917
return;
19091918
}
19101919

1911-
_qed_get_vport_stats(cdev, stats);
1920+
_qed_get_vport_stats(cdev, stats, is_atomic);
19121921

19131922
if (!cdev->reset_stats)
19141923
return;
@@ -1960,7 +1969,7 @@ void qed_reset_vport_stats(struct qed_dev *cdev)
19601969
if (!cdev->reset_stats) {
19611970
DP_INFO(cdev, "Reset stats not allocated\n");
19621971
} else {
1963-
_qed_get_vport_stats(cdev, cdev->reset_stats);
1972+
_qed_get_vport_stats(cdev, cdev->reset_stats, false);
19641973
cdev->reset_stats->common.link_change_count = 0;
19651974
}
19661975
}

drivers/net/ethernet/qlogic/qed/qed_l2.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,32 @@ qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn,
249249
enum spq_mode comp_mode,
250250
struct qed_spq_comp_cb *p_comp_data);
251251

252+
/**
253+
* qed_get_vport_stats(): Fills provided statistics
254+
* struct with statistics.
255+
*
256+
* @cdev: Qed dev pointer.
257+
* @stats: Points to struct that will be filled with statistics.
258+
*
259+
* Return: Void.
260+
*/
252261
void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats);
253262

263+
/**
264+
* qed_get_vport_stats_context(): Fills provided statistics
265+
* struct with statistics.
266+
*
267+
* @cdev: Qed dev pointer.
268+
* @stats: Points to struct that will be filled with statistics.
269+
* @is_atomic: Hint from the caller - if the func can sleep or not.
270+
*
271+
* Context: The function should not sleep in case is_atomic == true.
272+
* Return: Void.
273+
*/
274+
void qed_get_vport_stats_context(struct qed_dev *cdev,
275+
struct qed_eth_stats *stats,
276+
bool is_atomic);
277+
254278
void qed_reset_vport_stats(struct qed_dev *cdev);
255279

256280
/**

drivers/net/ethernet/qlogic/qed/qed_main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,18 +3092,18 @@ void qed_get_protocol_stats(struct qed_dev *cdev,
30923092

30933093
switch (type) {
30943094
case QED_MCP_LAN_STATS:
3095-
qed_get_vport_stats(cdev, &eth_stats);
3095+
qed_get_vport_stats_context(cdev, &eth_stats, true);
30963096
stats->lan_stats.ucast_rx_pkts =
30973097
eth_stats.common.rx_ucast_pkts;
30983098
stats->lan_stats.ucast_tx_pkts =
30993099
eth_stats.common.tx_ucast_pkts;
31003100
stats->lan_stats.fcs_err = -1;
31013101
break;
31023102
case QED_MCP_FCOE_STATS:
3103-
qed_get_protocol_stats_fcoe(cdev, &stats->fcoe_stats);
3103+
qed_get_protocol_stats_fcoe(cdev, &stats->fcoe_stats, true);
31043104
break;
31053105
case QED_MCP_ISCSI_STATS:
3106-
qed_get_protocol_stats_iscsi(cdev, &stats->iscsi_stats);
3106+
qed_get_protocol_stats_iscsi(cdev, &stats->iscsi_stats, true);
31073107
break;
31083108
default:
31093109
DP_VERBOSE(cdev, QED_MSG_SP,

0 commit comments

Comments
 (0)