Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <shannon.nelson@amd.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Shannon Nelson and committed by
David S. Miller
01ba61b5 45d76f49

+299 -12
+1
drivers/net/ethernet/amd/pds_core/Makefile
··· 6 6 pds_core-y := main.o \ 7 7 devlink.o \ 8 8 dev.o \ 9 + adminq.o \ 9 10 core.o 10 11 11 12 pds_core-$(CONFIG_DEBUG_FS) += debugfs.o
+288
drivers/net/ethernet/amd/pds_core/adminq.c
··· 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);
-11
drivers/net/ethernet/amd/pds_core/core.c
··· 6 6 7 7 #include "core.h" 8 8 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 - 20 9 void pdsc_intr_free(struct pdsc *pdsc, int index) 21 10 { 22 11 struct pdsc_intr_info *intr_info;
+10 -1
include/linux/pds/pds_adminq.h
··· 4 4 #ifndef _PDS_CORE_ADMINQ_H_ 5 5 #define _PDS_CORE_ADMINQ_H_ 6 6 7 + #define PDSC_ADMINQ_MAX_POLL_INTERVAL 256 8 + 7 9 enum pds_core_adminq_flags { 8 10 PDS_AQ_FLAG_FASTPOLL = BIT(1), /* completion poll at 1ms */ 9 11 }; ··· 633 631 * where the meaning alternates between '1' and '0' for alternating 634 632 * passes through the completion descriptor ring. 635 633 */ 636 - static inline u8 pdsc_color_match(u8 color, u8 done_color) 634 + static inline bool pdsc_color_match(u8 color, bool done_color) 637 635 { 638 636 return (!!(color & PDS_COMP_COLOR_MASK)) == done_color; 639 637 } 638 + 639 + struct pdsc; 640 + int pdsc_adminq_post(struct pdsc *pdsc, 641 + union pds_core_adminq_cmd *cmd, 642 + union pds_core_adminq_comp *comp, 643 + bool fast_poll); 644 + 640 645 #endif /* _PDS_CORE_ADMINQ_H_ */