Commit 0d72c2ac authored by Denis Bolotin's avatar Denis Bolotin Committed by David S. Miller

qed: Fix the DORQ's attentions handling

Separate the overflow handling from the hardware interrupt status analysis.
The interrupt status is a single register and is common for all PFs. The
first PF reading the register is not necessarily the one who overflowed.
All PFs must check their overflow status on every attention.
In this change we clear the sticky indication in the attention handler to
allow doorbells to be processed again as soon as possible, but running
the doorbell recovery is scheduled for the periodic handler to reduce the
time spent in the attention handler.
Checking the need for DORQ flush was changed to "db_bar_no_edpm" because
qed_edpm_enabled()'s result could change dynamically and might have
prevented a needed flush.
Signed-off-by: default avatarDenis Bolotin <dbolotin@marvell.com>
Signed-off-by: default avatarMichal Kalderon <mkalderon@marvell.com>
Signed-off-by: default avatarAriel Elior <aelior@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d4476b8a
...@@ -431,6 +431,8 @@ struct qed_qm_info { ...@@ -431,6 +431,8 @@ struct qed_qm_info {
u8 num_pf_rls; u8 num_pf_rls;
}; };
#define QED_OVERFLOW_BIT 1
struct qed_db_recovery_info { struct qed_db_recovery_info {
struct list_head list; struct list_head list;
...@@ -438,6 +440,7 @@ struct qed_db_recovery_info { ...@@ -438,6 +440,7 @@ struct qed_db_recovery_info {
spinlock_t lock; spinlock_t lock;
bool dorq_attn; bool dorq_attn;
u32 db_recovery_counter; u32 db_recovery_counter;
unsigned long overflow;
}; };
struct storm_stats { struct storm_stats {
......
...@@ -378,6 +378,9 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn, ...@@ -378,6 +378,9 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
u32 count = QED_DB_REC_COUNT; u32 count = QED_DB_REC_COUNT;
u32 usage = 1; u32 usage = 1;
/* Flush any pending (e)dpms as they may never arrive */
qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
/* wait for usage to zero or count to run out. This is necessary since /* wait for usage to zero or count to run out. This is necessary since
* EDPM doorbell transactions can take multiple 64b cycles, and as such * EDPM doorbell transactions can take multiple 64b cycles, and as such
* can "split" over the pci. Possibly, the doorbell drop can happen with * can "split" over the pci. Possibly, the doorbell drop can happen with
...@@ -406,23 +409,24 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn, ...@@ -406,23 +409,24 @@ static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{ {
u32 overflow; u32 attn_ovfl, cur_ovfl;
int rc; int rc;
overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY); attn_ovfl = test_and_clear_bit(QED_OVERFLOW_BIT,
DP_NOTICE(p_hwfn, "PF Overflow sticky 0x%x\n", overflow); &p_hwfn->db_recovery_info.overflow);
if (!overflow) cur_ovfl = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
if (!cur_ovfl && !attn_ovfl)
return 0; return 0;
if (qed_edpm_enabled(p_hwfn)) { DP_NOTICE(p_hwfn, "PF Overflow sticky: attn %u current %u\n",
attn_ovfl, cur_ovfl);
if (cur_ovfl && !p_hwfn->db_bar_no_edpm) {
rc = qed_db_rec_flush_queue(p_hwfn, p_ptt); rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
if (rc) if (rc)
return rc; return rc;
} }
/* Flush any pending (e)dpm as they may never arrive */
qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
/* Release overflow sticky indication (stop silently dropping everything) */ /* Release overflow sticky indication (stop silently dropping everything) */
qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0); qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
...@@ -432,13 +436,35 @@ int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) ...@@ -432,13 +436,35 @@ int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return 0; return 0;
} }
static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn) static void qed_dorq_attn_overflow(struct qed_hwfn *p_hwfn)
{ {
u32 int_sts, first_drop_reason, details, address, all_drops_reason;
struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt; struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
u32 overflow;
int rc; int rc;
p_hwfn->db_recovery_info.dorq_attn = true; overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
if (!overflow)
goto out;
/* Run PF doorbell recovery in next periodic handler */
set_bit(QED_OVERFLOW_BIT, &p_hwfn->db_recovery_info.overflow);
if (!p_hwfn->db_bar_no_edpm) {
rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
if (rc)
goto out;
}
qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
out:
/* Schedule the handler even if overflow was not detected */
qed_periodic_db_rec_start(p_hwfn);
}
static int qed_dorq_attn_int_sts(struct qed_hwfn *p_hwfn)
{
u32 int_sts, first_drop_reason, details, address, all_drops_reason;
struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
/* int_sts may be zero since all PFs were interrupted for doorbell /* int_sts may be zero since all PFs were interrupted for doorbell
* overflow but another one already handled it. Can abort here. If * overflow but another one already handled it. Can abort here. If
...@@ -477,11 +503,6 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn) ...@@ -477,11 +503,6 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4, GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
first_drop_reason, all_drops_reason); first_drop_reason, all_drops_reason);
rc = qed_db_rec_handler(p_hwfn, p_ptt);
qed_periodic_db_rec_start(p_hwfn);
if (rc)
return rc;
/* Clear the doorbell drop details and prepare for next drop */ /* Clear the doorbell drop details and prepare for next drop */
qed_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0); qed_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0);
...@@ -507,6 +528,14 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn) ...@@ -507,6 +528,14 @@ static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
return -EINVAL; return -EINVAL;
} }
static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
{
p_hwfn->db_recovery_info.dorq_attn = true;
qed_dorq_attn_overflow(p_hwfn);
return qed_dorq_attn_int_sts(p_hwfn);
}
static void qed_dorq_attn_handler(struct qed_hwfn *p_hwfn) static void qed_dorq_attn_handler(struct qed_hwfn *p_hwfn)
{ {
if (p_hwfn->db_recovery_info.dorq_attn) if (p_hwfn->db_recovery_info.dorq_attn)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment