Skip to content

Commit a6b16d8

Browse files
committed
Merge branch 'qed-doorbell-overflow-recovery'
Denis Bolotin says: ==================== qed: Fix the Doorbell Overflow Recovery mechanism This patch series fixes and improves the doorbell recovery mechanism. The main goals of this series are to fix missing attentions from the doorbells block (DORQ) or not handling them properly, and execute the recovery from periodic handler instead of the attention handler. Please consider applying the series to net. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents c543cb4 + 0d72c2a commit a6b16d8

File tree

5 files changed

+105
-76
lines changed

5 files changed

+105
-76
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,12 +431,16 @@ struct qed_qm_info {
431431
u8 num_pf_rls;
432432
};
433433

434+
#define QED_OVERFLOW_BIT 1
435+
434436
struct qed_db_recovery_info {
435437
struct list_head list;
436438

437439
/* Lock to protect the doorbell recovery mechanism list */
438440
spinlock_t lock;
441+
bool dorq_attn;
439442
u32 db_recovery_counter;
443+
unsigned long overflow;
440444
};
441445

442446
struct storm_stats {
@@ -920,8 +924,7 @@ u16 qed_get_cm_pq_idx_llt_mtc(struct qed_hwfn *p_hwfn, u8 tc);
920924

921925
/* doorbell recovery mechanism */
922926
void qed_db_recovery_dp(struct qed_hwfn *p_hwfn);
923-
void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
924-
enum qed_db_rec_exec db_exec);
927+
void qed_db_recovery_execute(struct qed_hwfn *p_hwfn);
925928
bool qed_edpm_enabled(struct qed_hwfn *p_hwfn);
926929

927930
/* Other Linux specific common definitions */

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

Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ static void qed_db_recovery_dp_entry(struct qed_hwfn *p_hwfn,
102102

103103
/* Doorbell address sanity (address within doorbell bar range) */
104104
static bool qed_db_rec_sanity(struct qed_dev *cdev,
105-
void __iomem *db_addr, void *db_data)
105+
void __iomem *db_addr,
106+
enum qed_db_rec_width db_width,
107+
void *db_data)
106108
{
109+
u32 width = (db_width == DB_REC_WIDTH_32B) ? 32 : 64;
110+
107111
/* Make sure doorbell address is within the doorbell bar */
108112
if (db_addr < cdev->doorbells ||
109-
(u8 __iomem *)db_addr >
113+
(u8 __iomem *)db_addr + width >
110114
(u8 __iomem *)cdev->doorbells + cdev->db_size) {
111115
WARN(true,
112116
"Illegal doorbell address: %p. Legal range for doorbell addresses is [%p..%p]\n",
@@ -159,7 +163,7 @@ int qed_db_recovery_add(struct qed_dev *cdev,
159163
}
160164

161165
/* Sanitize doorbell address */
162-
if (!qed_db_rec_sanity(cdev, db_addr, db_data))
166+
if (!qed_db_rec_sanity(cdev, db_addr, db_width, db_data))
163167
return -EINVAL;
164168

165169
/* Obtain hwfn from doorbell address */
@@ -205,10 +209,6 @@ int qed_db_recovery_del(struct qed_dev *cdev,
205209
return 0;
206210
}
207211

208-
/* Sanitize doorbell address */
209-
if (!qed_db_rec_sanity(cdev, db_addr, db_data))
210-
return -EINVAL;
211-
212212
/* Obtain hwfn from doorbell address */
213213
p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);
214214

@@ -300,31 +300,24 @@ void qed_db_recovery_dp(struct qed_hwfn *p_hwfn)
300300

301301
/* Ring the doorbell of a single doorbell recovery entry */
302302
static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
303-
struct qed_db_recovery_entry *db_entry,
304-
enum qed_db_rec_exec db_exec)
305-
{
306-
if (db_exec != DB_REC_ONCE) {
307-
/* Print according to width */
308-
if (db_entry->db_width == DB_REC_WIDTH_32B) {
309-
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
310-
"%s doorbell address %p data %x\n",
311-
db_exec == DB_REC_DRY_RUN ?
312-
"would have rung" : "ringing",
313-
db_entry->db_addr,
314-
*(u32 *)db_entry->db_data);
315-
} else {
316-
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
317-
"%s doorbell address %p data %llx\n",
318-
db_exec == DB_REC_DRY_RUN ?
319-
"would have rung" : "ringing",
320-
db_entry->db_addr,
321-
*(u64 *)(db_entry->db_data));
322-
}
303+
struct qed_db_recovery_entry *db_entry)
304+
{
305+
/* Print according to width */
306+
if (db_entry->db_width == DB_REC_WIDTH_32B) {
307+
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
308+
"ringing doorbell address %p data %x\n",
309+
db_entry->db_addr,
310+
*(u32 *)db_entry->db_data);
311+
} else {
312+
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
313+
"ringing doorbell address %p data %llx\n",
314+
db_entry->db_addr,
315+
*(u64 *)(db_entry->db_data));
323316
}
324317

325318
/* Sanity */
326319
if (!qed_db_rec_sanity(p_hwfn->cdev, db_entry->db_addr,
327-
db_entry->db_data))
320+
db_entry->db_width, db_entry->db_data))
328321
return;
329322

330323
/* Flush the write combined buffer. Since there are multiple doorbelling
@@ -334,14 +327,12 @@ static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
334327
wmb();
335328

336329
/* Ring the doorbell */
337-
if (db_exec == DB_REC_REAL_DEAL || db_exec == DB_REC_ONCE) {
338-
if (db_entry->db_width == DB_REC_WIDTH_32B)
339-
DIRECT_REG_WR(db_entry->db_addr,
340-
*(u32 *)(db_entry->db_data));
341-
else
342-
DIRECT_REG_WR64(db_entry->db_addr,
343-
*(u64 *)(db_entry->db_data));
344-
}
330+
if (db_entry->db_width == DB_REC_WIDTH_32B)
331+
DIRECT_REG_WR(db_entry->db_addr,
332+
*(u32 *)(db_entry->db_data));
333+
else
334+
DIRECT_REG_WR64(db_entry->db_addr,
335+
*(u64 *)(db_entry->db_data));
345336

346337
/* Flush the write combined buffer. Next doorbell may come from a
347338
* different entity to the same address...
@@ -350,29 +341,21 @@ static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
350341
}
351342

352343
/* Traverse the doorbell recovery entry list and ring all the doorbells */
353-
void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
354-
enum qed_db_rec_exec db_exec)
344+
void qed_db_recovery_execute(struct qed_hwfn *p_hwfn)
355345
{
356346
struct qed_db_recovery_entry *db_entry = NULL;
357347

358-
if (db_exec != DB_REC_ONCE) {
359-
DP_NOTICE(p_hwfn,
360-
"Executing doorbell recovery. Counter was %d\n",
361-
p_hwfn->db_recovery_info.db_recovery_counter);
348+
DP_NOTICE(p_hwfn, "Executing doorbell recovery. Counter was %d\n",
349+
p_hwfn->db_recovery_info.db_recovery_counter);
362350

363-
/* Track amount of times recovery was executed */
364-
p_hwfn->db_recovery_info.db_recovery_counter++;
365-
}
351+
/* Track amount of times recovery was executed */
352+
p_hwfn->db_recovery_info.db_recovery_counter++;
366353

367354
/* Protect the list */
368355
spin_lock_bh(&p_hwfn->db_recovery_info.lock);
369356
list_for_each_entry(db_entry,
370-
&p_hwfn->db_recovery_info.list, list_entry) {
371-
qed_db_recovery_ring(p_hwfn, db_entry, db_exec);
372-
if (db_exec == DB_REC_ONCE)
373-
break;
374-
}
375-
357+
&p_hwfn->db_recovery_info.list, list_entry)
358+
qed_db_recovery_ring(p_hwfn, db_entry);
376359
spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
377360
}
378361

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

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
378378
u32 count = QED_DB_REC_COUNT;
379379
u32 usage = 1;
380380

381+
/* Flush any pending (e)dpms as they may never arrive */
382+
qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
383+
381384
/* wait for usage to zero or count to run out. This is necessary since
382385
* EDPM doorbell transactions can take multiple 64b cycles, and as such
383386
* can "split" over the pci. Possibly, the doorbell drop can happen with
@@ -406,51 +409,74 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
406409

407410
int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
408411
{
409-
u32 overflow;
412+
u32 attn_ovfl, cur_ovfl;
410413
int rc;
411414

412-
overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
413-
DP_NOTICE(p_hwfn, "PF Overflow sticky 0x%x\n", overflow);
414-
if (!overflow) {
415-
qed_db_recovery_execute(p_hwfn, DB_REC_ONCE);
415+
attn_ovfl = test_and_clear_bit(QED_OVERFLOW_BIT,
416+
&p_hwfn->db_recovery_info.overflow);
417+
cur_ovfl = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
418+
if (!cur_ovfl && !attn_ovfl)
416419
return 0;
417-
}
418420

419-
if (qed_edpm_enabled(p_hwfn)) {
421+
DP_NOTICE(p_hwfn, "PF Overflow sticky: attn %u current %u\n",
422+
attn_ovfl, cur_ovfl);
423+
424+
if (cur_ovfl && !p_hwfn->db_bar_no_edpm) {
420425
rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
421426
if (rc)
422427
return rc;
423428
}
424429

425-
/* Flush any pending (e)dpm as they may never arrive */
426-
qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
427-
428430
/* Release overflow sticky indication (stop silently dropping everything) */
429431
qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
430432

431433
/* Repeat all last doorbells (doorbell drop recovery) */
432-
qed_db_recovery_execute(p_hwfn, DB_REC_REAL_DEAL);
434+
qed_db_recovery_execute(p_hwfn);
433435

434436
return 0;
435437
}
436438

437-
static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
439+
static void qed_dorq_attn_overflow(struct qed_hwfn *p_hwfn)
438440
{
439-
u32 int_sts, first_drop_reason, details, address, all_drops_reason;
440441
struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
442+
u32 overflow;
441443
int rc;
442444

443-
int_sts = qed_rd(p_hwfn, p_ptt, DORQ_REG_INT_STS);
444-
DP_NOTICE(p_hwfn->cdev, "DORQ attention. int_sts was %x\n", int_sts);
445+
overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
446+
if (!overflow)
447+
goto out;
448+
449+
/* Run PF doorbell recovery in next periodic handler */
450+
set_bit(QED_OVERFLOW_BIT, &p_hwfn->db_recovery_info.overflow);
451+
452+
if (!p_hwfn->db_bar_no_edpm) {
453+
rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
454+
if (rc)
455+
goto out;
456+
}
457+
458+
qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
459+
out:
460+
/* Schedule the handler even if overflow was not detected */
461+
qed_periodic_db_rec_start(p_hwfn);
462+
}
463+
464+
static int qed_dorq_attn_int_sts(struct qed_hwfn *p_hwfn)
465+
{
466+
u32 int_sts, first_drop_reason, details, address, all_drops_reason;
467+
struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
445468

446469
/* int_sts may be zero since all PFs were interrupted for doorbell
447470
* overflow but another one already handled it. Can abort here. If
448471
* This PF also requires overflow recovery we will be interrupted again.
449472
* The masked almost full indication may also be set. Ignoring.
450473
*/
474+
int_sts = qed_rd(p_hwfn, p_ptt, DORQ_REG_INT_STS);
451475
if (!(int_sts & ~DORQ_REG_INT_STS_DORQ_FIFO_AFULL))
452476
return 0;
453477

478+
DP_NOTICE(p_hwfn->cdev, "DORQ attention. int_sts was %x\n", int_sts);
479+
454480
/* check if db_drop or overflow happened */
455481
if (int_sts & (DORQ_REG_INT_STS_DB_DROP |
456482
DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR)) {
@@ -477,11 +503,6 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
477503
GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
478504
first_drop_reason, all_drops_reason);
479505

480-
rc = qed_db_rec_handler(p_hwfn, p_ptt);
481-
qed_periodic_db_rec_start(p_hwfn);
482-
if (rc)
483-
return rc;
484-
485506
/* Clear the doorbell drop details and prepare for next drop */
486507
qed_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0);
487508

@@ -507,6 +528,25 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
507528
return -EINVAL;
508529
}
509530

531+
static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
532+
{
533+
p_hwfn->db_recovery_info.dorq_attn = true;
534+
qed_dorq_attn_overflow(p_hwfn);
535+
536+
return qed_dorq_attn_int_sts(p_hwfn);
537+
}
538+
539+
static void qed_dorq_attn_handler(struct qed_hwfn *p_hwfn)
540+
{
541+
if (p_hwfn->db_recovery_info.dorq_attn)
542+
goto out;
543+
544+
/* Call DORQ callback if the attention was missed */
545+
qed_dorq_attn_cb(p_hwfn);
546+
out:
547+
p_hwfn->db_recovery_info.dorq_attn = false;
548+
}
549+
510550
/* Instead of major changes to the data-structure, we have a some 'special'
511551
* identifiers for sources that changed meaning between adapters.
512552
*/
@@ -1080,6 +1120,9 @@ static int qed_int_deassertion(struct qed_hwfn *p_hwfn,
10801120
}
10811121
}
10821122

1123+
/* Handle missed DORQ attention */
1124+
qed_dorq_attn_handler(p_hwfn);
1125+
10831126
/* Clear IGU indication for the deasserted bits */
10841127
DIRECT_REG_WR((u8 __iomem *)p_hwfn->regview +
10851128
GTT_BAR0_MAP_REG_IGU_CMD +

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ void qed_int_disable_post_isr_release(struct qed_dev *cdev);
192192

193193
/**
194194
* @brief - Doorbell Recovery handler.
195-
* Run DB_REAL_DEAL doorbell recovery in case of PF overflow
196-
* (and flush DORQ if needed), otherwise run DB_REC_ONCE.
195+
* Run doorbell recovery in case of PF overflow (and flush DORQ if
196+
* needed).
197197
*
198198
* @param p_hwfn
199199
* @param p_ptt

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,7 @@ static void qed_update_pf_params(struct qed_dev *cdev,
970970
}
971971
}
972972

973-
#define QED_PERIODIC_DB_REC_COUNT 100
973+
#define QED_PERIODIC_DB_REC_COUNT 10
974974
#define QED_PERIODIC_DB_REC_INTERVAL_MS 100
975975
#define QED_PERIODIC_DB_REC_INTERVAL \
976976
msecs_to_jiffies(QED_PERIODIC_DB_REC_INTERVAL_MS)

0 commit comments

Comments
 (0)