Skip to content

Commit ccadf13

Browse files
Davidlohr Buesodjbw
authored andcommitted
cxl/mbox: Add background cmd handling machinery
This adds support for handling background operations, as defined in the CXL 3.0 spec. Commands that can take too long (over ~2 seconds) can run in the background asynchronously (to the hardware). The driver will deal with such commands synchronously, blocking all other incoming commands for a specified period of time, allowing time-slicing the command such that the caller can send incremental requests to avoid monopolizing the driver/device. Any out of sync (timeout) between the driver and hardware is just disregarded as an invalid state until the next successful submission. Such timeouts are considered a rare occurrence, either a real device problem or a driver issue that needs to reduce the size of the background operation to fit the timeout. On devices where mbox interrupts are supported, this will still use a poller that will wakeup in the specified wait intervals. The irq handler will simply awake the blocked cmd, which is also safe vs a task that is either waking (timing out) or already awoken. Similarly any irq setup error during the probing falls back to polling, thus avoids unnecessarily erroring out. Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://lore.kernel.org/r/20230523170927.20685-5-dave@stgolabs.net Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 9f7a320 commit ccadf13

4 files changed

Lines changed: 106 additions & 1 deletion

File tree

drivers/cxl/core/mbox.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ int cxl_internal_send_cmd(struct cxl_dev_state *cxlds,
220220
if (rc)
221221
return rc;
222222

223-
if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS)
223+
if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS &&
224+
mbox_cmd->return_code != CXL_MBOX_CMD_RC_BACKGROUND)
224225
return cxl_mbox_cmd_rc2errno(mbox_cmd);
225226

226227
if (!out_size)

drivers/cxl/cxl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,22 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
176176
/* CXL 2.0 8.2.8.4 Mailbox Registers */
177177
#define CXLDEV_MBOX_CAPS_OFFSET 0x00
178178
#define CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK GENMASK(4, 0)
179+
#define CXLDEV_MBOX_CAP_BG_CMD_IRQ BIT(6)
180+
#define CXLDEV_MBOX_CAP_IRQ_MSGNUM_MASK GENMASK(10, 7)
179181
#define CXLDEV_MBOX_CTRL_OFFSET 0x04
180182
#define CXLDEV_MBOX_CTRL_DOORBELL BIT(0)
183+
#define CXLDEV_MBOX_CTRL_BG_CMD_IRQ BIT(2)
181184
#define CXLDEV_MBOX_CMD_OFFSET 0x08
182185
#define CXLDEV_MBOX_CMD_COMMAND_OPCODE_MASK GENMASK_ULL(15, 0)
183186
#define CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK GENMASK_ULL(36, 16)
184187
#define CXLDEV_MBOX_STATUS_OFFSET 0x10
188+
#define CXLDEV_MBOX_STATUS_BG_CMD BIT(0)
185189
#define CXLDEV_MBOX_STATUS_RET_CODE_MASK GENMASK_ULL(47, 32)
186190
#define CXLDEV_MBOX_BG_CMD_STATUS_OFFSET 0x18
191+
#define CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK GENMASK_ULL(15, 0)
192+
#define CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK GENMASK_ULL(22, 16)
193+
#define CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK GENMASK_ULL(47, 32)
194+
#define CXLDEV_MBOX_BG_CMD_COMMAND_VENDOR_MASK GENMASK_ULL(63, 48)
187195
#define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20
188196

189197
/*

drivers/cxl/cxlmem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <uapi/linux/cxl_mem.h>
66
#include <linux/cdev.h>
77
#include <linux/uuid.h>
8+
#include <linux/rcuwait.h>
89
#include "cxl.h"
910

1011
/* CXL 2.0 8.2.8.5.1.1 Memory Device Status Register */
@@ -108,6 +109,9 @@ static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port,
108109
* variable sized output commands, it tells the exact number of bytes
109110
* written.
110111
* @min_out: (input) internal command output payload size validation
112+
* @poll_count: (input) Number of timeouts to attempt.
113+
* @poll_interval_ms: (input) Time between mailbox background command polling
114+
* interval timeouts.
111115
* @return_code: (output) Error code returned from hardware.
112116
*
113117
* This is the primary mechanism used to send commands to the hardware.
@@ -123,6 +127,8 @@ struct cxl_mbox_cmd {
123127
size_t size_in;
124128
size_t size_out;
125129
size_t min_out;
130+
int poll_count;
131+
int poll_interval_ms;
126132
u16 return_code;
127133
};
128134

@@ -331,6 +337,7 @@ struct cxl_dev_state {
331337
struct cxl_event_state event;
332338
struct cxl_poison_state poison;
333339

340+
struct rcuwait mbox_wait;
334341
int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
335342
};
336343

drivers/cxl/pci.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@ static int cxl_request_irq(struct cxl_dev_state *cxlds, int irq,
105105
NULL, dev_id);
106106
}
107107

108+
static bool cxl_mbox_background_complete(struct cxl_dev_state *cxlds)
109+
{
110+
u64 reg;
111+
112+
reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
113+
return FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg) == 100;
114+
}
115+
116+
static irqreturn_t cxl_pci_mbox_irq(int irq, void *id)
117+
{
118+
struct cxl_dev_id *dev_id = id;
119+
struct cxl_dev_state *cxlds = dev_id->cxlds;
120+
121+
/* short-circuit the wait in __cxl_pci_mbox_send_cmd() */
122+
if (cxl_mbox_background_complete(cxlds))
123+
rcuwait_wake_up(&cxlds->mbox_wait);
124+
125+
return IRQ_HANDLED;
126+
}
127+
108128
/**
109129
* __cxl_pci_mbox_send_cmd() - Execute a mailbox command
110130
* @cxlds: The device state to communicate with.
@@ -198,6 +218,50 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
198218
mbox_cmd->return_code =
199219
FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
200220

221+
/*
222+
* Handle the background command in a synchronous manner.
223+
*
224+
* All other mailbox commands will serialize/queue on the mbox_mutex,
225+
* which we currently hold. Furthermore this also guarantees that
226+
* cxl_mbox_background_complete() checks are safe amongst each other,
227+
* in that no new bg operation can occur in between.
228+
*
229+
* Background operations are timesliced in accordance with the nature
230+
* of the command. In the event of timeout, the mailbox state is
231+
* indeterminate until the next successful command submission and the
232+
* driver can get back in sync with the hardware state.
233+
*/
234+
if (mbox_cmd->return_code == CXL_MBOX_CMD_RC_BACKGROUND) {
235+
u64 bg_status_reg;
236+
int i, timeout = mbox_cmd->poll_interval_ms;
237+
238+
dev_dbg(dev, "Mailbox background operation (0x%04x) started\n",
239+
mbox_cmd->opcode);
240+
241+
for (i = 0; i < mbox_cmd->poll_count; i++) {
242+
if (rcuwait_wait_event_timeout(&cxlds->mbox_wait,
243+
cxl_mbox_background_complete(cxlds),
244+
TASK_UNINTERRUPTIBLE,
245+
msecs_to_jiffies(timeout)) > 0)
246+
break;
247+
}
248+
249+
if (!cxl_mbox_background_complete(cxlds)) {
250+
dev_err(dev, "timeout waiting for background (%d ms)\n",
251+
timeout * mbox_cmd->poll_count);
252+
return -ETIMEDOUT;
253+
}
254+
255+
bg_status_reg = readq(cxlds->regs.mbox +
256+
CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
257+
mbox_cmd->return_code =
258+
FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK,
259+
bg_status_reg);
260+
dev_dbg(dev,
261+
"Mailbox background operation (0x%04x) completed\n",
262+
mbox_cmd->opcode);
263+
}
264+
201265
if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS) {
202266
dev_dbg(dev, "Mailbox operation had an error: %s\n",
203267
cxl_mbox_cmd_rc2str(mbox_cmd));
@@ -292,6 +356,31 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
292356
dev_dbg(cxlds->dev, "Mailbox payload sized %zu",
293357
cxlds->payload_size);
294358

359+
rcuwait_init(&cxlds->mbox_wait);
360+
361+
if (cap & CXLDEV_MBOX_CAP_BG_CMD_IRQ) {
362+
u32 ctrl;
363+
int irq, msgnum;
364+
struct pci_dev *pdev = to_pci_dev(cxlds->dev);
365+
366+
msgnum = FIELD_GET(CXLDEV_MBOX_CAP_IRQ_MSGNUM_MASK, cap);
367+
irq = pci_irq_vector(pdev, msgnum);
368+
if (irq < 0)
369+
goto mbox_poll;
370+
371+
if (cxl_request_irq(cxlds, irq, cxl_pci_mbox_irq, NULL))
372+
goto mbox_poll;
373+
374+
/* enable background command mbox irq support */
375+
ctrl = readl(cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
376+
ctrl |= CXLDEV_MBOX_CTRL_BG_CMD_IRQ;
377+
writel(ctrl, cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
378+
379+
return 0;
380+
}
381+
382+
mbox_poll:
383+
dev_dbg(cxlds->dev, "Mailbox interrupts are unsupported");
295384
return 0;
296385
}
297386

0 commit comments

Comments
 (0)