Skip to content

Commit 01ba61b

Browse files
emuslndavem330
authored andcommitted
pds_core: Add adminq processing and commands
Add the service routines for submitting and processing the adminq messages and for handling notifyq events. Signed-off-by: Shannon Nelson <[email protected]> Acked-by: Jakub Kicinski <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 45d76f4 commit 01ba61b

File tree

4 files changed

+299
-12
lines changed

4 files changed

+299
-12
lines changed

drivers/net/ethernet/amd/pds_core/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ obj-$(CONFIG_PDS_CORE) := pds_core.o
66
pds_core-y := main.o \
77
devlink.o \
88
dev.o \
9+
adminq.o \
910
core.o
1011

1112
pds_core-$(CONFIG_DEBUG_FS) += debugfs.o
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright(c) 2023 Advanced Micro Devices, Inc */
3+
4+
#include <linux/dynamic_debug.h>
5+
6+
#include "core.h"
7+
8+
struct pdsc_wait_context {
9+
struct pdsc_qcq *qcq;
10+
struct completion wait_completion;
11+
};
12+
13+
static int pdsc_process_notifyq(struct pdsc_qcq *qcq)
14+
{
15+
union pds_core_notifyq_comp *comp;
16+
struct pdsc *pdsc = qcq->pdsc;
17+
struct pdsc_cq *cq = &qcq->cq;
18+
struct pdsc_cq_info *cq_info;
19+
int nq_work = 0;
20+
u64 eid;
21+
22+
cq_info = &cq->info[cq->tail_idx];
23+
comp = cq_info->comp;
24+
eid = le64_to_cpu(comp->event.eid);
25+
while (eid > pdsc->last_eid) {
26+
u16 ecode = le16_to_cpu(comp->event.ecode);
27+
28+
switch (ecode) {
29+
case PDS_EVENT_LINK_CHANGE:
30+
dev_info(pdsc->dev, "NotifyQ LINK_CHANGE ecode %d eid %lld\n",
31+
ecode, eid);
32+
break;
33+
34+
case PDS_EVENT_RESET:
35+
dev_info(pdsc->dev, "NotifyQ RESET ecode %d eid %lld\n",
36+
ecode, eid);
37+
break;
38+
39+
case PDS_EVENT_XCVR:
40+
dev_info(pdsc->dev, "NotifyQ XCVR ecode %d eid %lld\n",
41+
ecode, eid);
42+
break;
43+
44+
default:
45+
dev_info(pdsc->dev, "NotifyQ ecode %d eid %lld\n",
46+
ecode, eid);
47+
break;
48+
}
49+
50+
pdsc->last_eid = eid;
51+
cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1);
52+
cq_info = &cq->info[cq->tail_idx];
53+
comp = cq_info->comp;
54+
eid = le64_to_cpu(comp->event.eid);
55+
56+
nq_work++;
57+
}
58+
59+
qcq->accum_work += nq_work;
60+
61+
return nq_work;
62+
}
63+
64+
void pdsc_process_adminq(struct pdsc_qcq *qcq)
65+
{
66+
union pds_core_adminq_comp *comp;
67+
struct pdsc_queue *q = &qcq->q;
68+
struct pdsc *pdsc = qcq->pdsc;
69+
struct pdsc_cq *cq = &qcq->cq;
70+
struct pdsc_q_info *q_info;
71+
unsigned long irqflags;
72+
int nq_work = 0;
73+
int aq_work = 0;
74+
int credits;
75+
76+
/* Don't process AdminQ when shutting down */
77+
if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) {
78+
dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n",
79+
__func__);
80+
return;
81+
}
82+
83+
/* Check for NotifyQ event */
84+
nq_work = pdsc_process_notifyq(&pdsc->notifyqcq);
85+
86+
/* Check for empty queue, which can happen if the interrupt was
87+
* for a NotifyQ event and there are no new AdminQ completions.
88+
*/
89+
if (q->tail_idx == q->head_idx)
90+
goto credits;
91+
92+
/* Find the first completion to clean,
93+
* run the callback in the related q_info,
94+
* and continue while we still match done color
95+
*/
96+
spin_lock_irqsave(&pdsc->adminq_lock, irqflags);
97+
comp = cq->info[cq->tail_idx].comp;
98+
while (pdsc_color_match(comp->color, cq->done_color)) {
99+
q_info = &q->info[q->tail_idx];
100+
q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
101+
102+
/* Copy out the completion data */
103+
memcpy(q_info->dest, comp, sizeof(*comp));
104+
105+
complete_all(&q_info->wc->wait_completion);
106+
107+
if (cq->tail_idx == cq->num_descs - 1)
108+
cq->done_color = !cq->done_color;
109+
cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1);
110+
comp = cq->info[cq->tail_idx].comp;
111+
112+
aq_work++;
113+
}
114+
spin_unlock_irqrestore(&pdsc->adminq_lock, irqflags);
115+
116+
qcq->accum_work += aq_work;
117+
118+
credits:
119+
/* Return the interrupt credits, one for each completion */
120+
credits = nq_work + aq_work;
121+
if (credits)
122+
pds_core_intr_credits(&pdsc->intr_ctrl[qcq->intx],
123+
credits,
124+
PDS_CORE_INTR_CRED_REARM);
125+
}
126+
127+
void pdsc_work_thread(struct work_struct *work)
128+
{
129+
struct pdsc_qcq *qcq = container_of(work, struct pdsc_qcq, work);
130+
131+
pdsc_process_adminq(qcq);
132+
}
133+
134+
irqreturn_t pdsc_adminq_isr(int irq, void *data)
135+
{
136+
struct pdsc_qcq *qcq = data;
137+
struct pdsc *pdsc = qcq->pdsc;
138+
139+
/* Don't process AdminQ when shutting down */
140+
if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) {
141+
dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n",
142+
__func__);
143+
return IRQ_HANDLED;
144+
}
145+
146+
queue_work(pdsc->wq, &qcq->work);
147+
pds_core_intr_mask(&pdsc->intr_ctrl[irq], PDS_CORE_INTR_MASK_CLEAR);
148+
149+
return IRQ_HANDLED;
150+
}
151+
152+
static int __pdsc_adminq_post(struct pdsc *pdsc,
153+
struct pdsc_qcq *qcq,
154+
union pds_core_adminq_cmd *cmd,
155+
union pds_core_adminq_comp *comp,
156+
struct pdsc_wait_context *wc)
157+
{
158+
struct pdsc_queue *q = &qcq->q;
159+
struct pdsc_q_info *q_info;
160+
unsigned long irqflags;
161+
unsigned int avail;
162+
int index;
163+
int ret;
164+
165+
spin_lock_irqsave(&pdsc->adminq_lock, irqflags);
166+
167+
/* Check for space in the queue */
168+
avail = q->tail_idx;
169+
if (q->head_idx >= avail)
170+
avail += q->num_descs - q->head_idx - 1;
171+
else
172+
avail -= q->head_idx + 1;
173+
if (!avail) {
174+
ret = -ENOSPC;
175+
goto err_out_unlock;
176+
}
177+
178+
/* Check that the FW is running */
179+
if (!pdsc_is_fw_running(pdsc)) {
180+
u8 fw_status = ioread8(&pdsc->info_regs->fw_status);
181+
182+
dev_info(pdsc->dev, "%s: post failed - fw not running %#02x:\n",
183+
__func__, fw_status);
184+
ret = -ENXIO;
185+
186+
goto err_out_unlock;
187+
}
188+
189+
/* Post the request */
190+
index = q->head_idx;
191+
q_info = &q->info[index];
192+
q_info->wc = wc;
193+
q_info->dest = comp;
194+
memcpy(q_info->desc, cmd, sizeof(*cmd));
195+
196+
dev_dbg(pdsc->dev, "head_idx %d tail_idx %d\n",
197+
q->head_idx, q->tail_idx);
198+
dev_dbg(pdsc->dev, "post admin queue command:\n");
199+
dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
200+
cmd, sizeof(*cmd), true);
201+
202+
q->head_idx = (q->head_idx + 1) & (q->num_descs - 1);
203+
204+
pds_core_dbell_ring(pdsc->kern_dbpage,
205+
q->hw_type, q->dbval | q->head_idx);
206+
ret = index;
207+
208+
err_out_unlock:
209+
spin_unlock_irqrestore(&pdsc->adminq_lock, irqflags);
210+
return ret;
211+
}
212+
213+
int pdsc_adminq_post(struct pdsc *pdsc,
214+
union pds_core_adminq_cmd *cmd,
215+
union pds_core_adminq_comp *comp,
216+
bool fast_poll)
217+
{
218+
struct pdsc_wait_context wc = {
219+
.wait_completion =
220+
COMPLETION_INITIALIZER_ONSTACK(wc.wait_completion),
221+
};
222+
unsigned long poll_interval = 1;
223+
unsigned long poll_jiffies;
224+
unsigned long time_limit;
225+
unsigned long time_start;
226+
unsigned long time_done;
227+
unsigned long remaining;
228+
int err = 0;
229+
int index;
230+
231+
wc.qcq = &pdsc->adminqcq;
232+
index = __pdsc_adminq_post(pdsc, &pdsc->adminqcq, cmd, comp, &wc);
233+
if (index < 0) {
234+
err = index;
235+
goto err_out;
236+
}
237+
238+
time_start = jiffies;
239+
time_limit = time_start + HZ * pdsc->devcmd_timeout;
240+
do {
241+
/* Timeslice the actual wait to catch IO errors etc early */
242+
poll_jiffies = msecs_to_jiffies(poll_interval);
243+
remaining = wait_for_completion_timeout(&wc.wait_completion,
244+
poll_jiffies);
245+
if (remaining)
246+
break;
247+
248+
if (!pdsc_is_fw_running(pdsc)) {
249+
u8 fw_status = ioread8(&pdsc->info_regs->fw_status);
250+
251+
dev_dbg(pdsc->dev, "%s: post wait failed - fw not running %#02x:\n",
252+
__func__, fw_status);
253+
err = -ENXIO;
254+
break;
255+
}
256+
257+
/* When fast_poll is not requested, prevent aggressive polling
258+
* on failures due to timeouts by doing exponential back off.
259+
*/
260+
if (!fast_poll && poll_interval < PDSC_ADMINQ_MAX_POLL_INTERVAL)
261+
poll_interval <<= 1;
262+
} while (time_before(jiffies, time_limit));
263+
time_done = jiffies;
264+
dev_dbg(pdsc->dev, "%s: elapsed %d msecs\n",
265+
__func__, jiffies_to_msecs(time_done - time_start));
266+
267+
/* Check the results */
268+
if (time_after_eq(time_done, time_limit))
269+
err = -ETIMEDOUT;
270+
271+
dev_dbg(pdsc->dev, "read admin queue completion idx %d:\n", index);
272+
dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1,
273+
comp, sizeof(*comp), true);
274+
275+
if (remaining && comp->status)
276+
err = pdsc_err_to_errno(comp->status);
277+
278+
err_out:
279+
if (err) {
280+
dev_dbg(pdsc->dev, "%s: opcode %d status %d err %pe\n",
281+
__func__, cmd->opcode, comp->status, ERR_PTR(err));
282+
if (err == -ENXIO || err == -ETIMEDOUT)
283+
queue_work(pdsc->wq, &pdsc->health_work);
284+
}
285+
286+
return err;
287+
}
288+
EXPORT_SYMBOL_GPL(pdsc_adminq_post);

drivers/net/ethernet/amd/pds_core/core.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@
66

77
#include "core.h"
88

9-
void pdsc_work_thread(struct work_struct *work)
10-
{
11-
/* stub */
12-
}
13-
14-
irqreturn_t pdsc_adminq_isr(int irq, void *data)
15-
{
16-
/* stub */
17-
return IRQ_HANDLED;
18-
}
19-
209
void pdsc_intr_free(struct pdsc *pdsc, int index)
2110
{
2211
struct pdsc_intr_info *intr_info;

include/linux/pds/pds_adminq.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#ifndef _PDS_CORE_ADMINQ_H_
55
#define _PDS_CORE_ADMINQ_H_
66

7+
#define PDSC_ADMINQ_MAX_POLL_INTERVAL 256
8+
79
enum pds_core_adminq_flags {
810
PDS_AQ_FLAG_FASTPOLL = BIT(1), /* completion poll at 1ms */
911
};
@@ -631,8 +633,15 @@ static_assert(sizeof(union pds_core_notifyq_comp) == 64);
631633
* where the meaning alternates between '1' and '0' for alternating
632634
* passes through the completion descriptor ring.
633635
*/
634-
static inline u8 pdsc_color_match(u8 color, u8 done_color)
636+
static inline bool pdsc_color_match(u8 color, bool done_color)
635637
{
636638
return (!!(color & PDS_COMP_COLOR_MASK)) == done_color;
637639
}
640+
641+
struct pdsc;
642+
int pdsc_adminq_post(struct pdsc *pdsc,
643+
union pds_core_adminq_cmd *cmd,
644+
union pds_core_adminq_comp *comp,
645+
bool fast_poll);
646+
638647
#endif /* _PDS_CORE_ADMINQ_H_ */

0 commit comments

Comments
 (0)