History

cxl/mem: Add basic IOCTL interface

Add a straightforward IOCTL that provides a mechanism for userspace to query the supported memory device commands. CXL commands as they appear to userspace are described as part of the UAPI kerneldoc. The command list returned via this IOCTL will contain the full set of commands that the driver supports, however, some of those commands may not be available for use by userspace. Memory device commands first appear in the CXL 2.0 specification. They are submitted through a mailbox mechanism specified in the CXL 2.0 specification. The send command allows userspace to issue mailbox commands directly to the hardware. The list of available commands to send are the output of the query command. The driver verifies basic properties of the command and possibly inspect the input (or output) payload to determine whether or not the command is allowed (or might taint the kernel).

Ben Widawsky committed 5 years ago

Commit 583fa5e1 parent b39cb105 files · +452 −1Browse files

Unified · Split

5 files
  1. .clang-format +1 −0
  2. Documentation/driver-api/cxl/memory-devices.rst +12 −0
  3. Documentation/userspace-api/ioctl/ioctl-number.rst +1 −0
  4. drivers/cxl/mem.c +282 −1
  5. include/uapi/linux/cxl_mem.h +156 −0
.clang-format+1−0
@@ -109,6 +109,7 @@ ForEachMacros: · 108 unmodified lines
109109 - 'css_for_each_child'
110110 - 'css_for_each_descendant_post'
111111 - 'css_for_each_descendant_pre'
112+ - 'cxl_for_each_cmd'
112113 - 'device_for_each_child_node'
113114 - 'dma_fence_chain_for_each'
114115 - 'do_for_each_ftrace_op'
Documentation/driver-api/cxl/memory-devices.rst+12−0
@@ -32,3 +32,15 @@ CXL Bus · 31 unmodified lines
3232 -------
3333 .. kernel-doc:: drivers/cxl/bus.c
3434 :doc: cxl bus
35+
36+External Interfaces
37+===================
38+
39+CXL IOCTL Interface
40+-------------------
41+
42+.. kernel-doc:: include/uapi/linux/cxl_mem.h
43+ :doc: UAPI
44+
45+.. kernel-doc:: include/uapi/linux/cxl_mem.h
46+ :internal:
Documentation/userspace-api/ioctl/ioctl-number.rst+1−0
@@ -352,6 +352,7 @@ Code Seq# Include File Comments · 351 unmodified lines
352352 <mailto:michael.klein@puffin.lb.shuttle.de>
353353 0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver
354354 0xCD 01 linux/reiserfs_fs.h
355+0xCE 01-02 uapi/linux/cxl_mem.h Compute Express Link Memory Devices
355356 0xCF 02 fs/cifs/ioctl.c
356357 0xDB 00-0F drivers/char/mwave/mwavepub.h
357358 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/
drivers/cxl/mem.c+282−1
@@ -1,5 +1,6 @@
11 // SPDX-License-Identifier: GPL-2.0-only
22 /* Copyright(c) 2020 Intel Corporation. All rights reserved. */
3+#include <uapi/linux/cxl_mem.h>
34 #include <linux/module.h>
45 #include <linux/mutex.h>
56 #include <linux/cdev.h>
@@ -40,6 +41,7 @@ · 34 unmodified lines
4041 #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
4142
4243 enum opcode {
44+ CXL_MBOX_OP_INVALID = 0x0000,
4345 CXL_MBOX_OP_IDENTIFY = 0x4000,
4446 CXL_MBOX_OP_MAX = 0x10000
4547 };
@@ -95,6 +97,49 @@ struct cxl_memdev { · 49 unmodified lines
9597 static int cxl_mem_major;
9698 static DEFINE_IDA(cxl_memdev_ida);
9799
100+/**
101+ * struct cxl_mem_command - Driver representation of a memory device command
102+ * @info: Command information as it exists for the UAPI
103+ * @opcode: The actual bits used for the mailbox protocol
104+ *
105+ * The cxl_mem_command is the driver's internal representation of commands that
106+ * are supported by the driver. Some of these commands may not be supported by
107+ * the hardware. The driver will use @info to validate the fields passed in by
108+ * the user then submit the @opcode to the hardware.
109+ *
110+ * See struct cxl_command_info.
111+ */
112+struct cxl_mem_command {
113+ struct cxl_command_info info;
114+ enum opcode opcode;
115+};
116+
117+#define CXL_CMD(_id, sin, sout) \
118+ [CXL_MEM_COMMAND_ID_##_id] = { \
119+ .info = { \
120+ .id = CXL_MEM_COMMAND_ID_##_id, \
121+ .size_in = sin, \
122+ .size_out = sout, \
123+ }, \
124+ .opcode = CXL_MBOX_OP_##_id, \
125+ }
126+
127+/*
128+ * This table defines the supported mailbox commands for the driver. This table
129+ * is made up of a UAPI structure. Non-negative values as parameters in the
130+ * table will be validated against the user's input. For example, if size_in is
131+ * 0, and the user passed in 1, it is an error.
132+ */
133+static struct cxl_mem_command mem_commands[] = {
134+ CXL_CMD(IDENTIFY, 0, 0x43),
135+};
136+
137+#define cxl_for_each_cmd(cmd) \
138+ for ((cmd) = &mem_commands[0]; \
139+ ((cmd) - mem_commands) < ARRAY_SIZE(mem_commands); (cmd)++)
140+
141+#define cxl_cmd_count ARRAY_SIZE(mem_commands)
142+
98143 static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
99144 {
100145 const unsigned long start = jiffies;
@@ -324,6 +369,242 @@ static void cxl_mem_mbox_put(struct cxl_mem *cxlm) · 223 unmodified lines
324369 mutex_unlock(&cxlm->mbox_mutex);
325370 }
326371
372+/**
373+ * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
374+ * @cxlm: The CXL memory device to communicate with.
375+ * @cmd: The validated command.
376+ * @in_payload: Pointer to userspace's input payload.
377+ * @out_payload: Pointer to userspace's output payload.
378+ * @size_out: (Input) Max payload size to copy out.
379+ * (Output) Payload size hardware generated.
380+ * @retval: Hardware generated return code from the operation.
381+ *
382+ * Return:
383+ * * %0 - Mailbox transaction succeeded. This implies the mailbox
384+ * protocol completed successfully not that the operation itself
385+ * was successful.
386+ * * %-ENOMEM - Couldn't allocate a bounce buffer.
387+ * * %-EFAULT - Something happened with copy_to/from_user.
388+ * * %-EINTR - Mailbox acquisition interrupted.
389+ * * %-EXXX - Transaction level failures.
390+ *
391+ * Creates the appropriate mailbox command and dispatches it on behalf of a
392+ * userspace request. The input and output payloads are copied between
393+ * userspace.
394+ *
395+ * See cxl_send_cmd().
396+ */
397+static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
398+ const struct cxl_mem_command *cmd,
399+ u64 in_payload, u64 out_payload,
400+ s32 *size_out, u32 *retval)
401+{
402+ struct device *dev = &cxlm->pdev->dev;
403+ struct mbox_cmd mbox_cmd = {
404+ .opcode = cmd->opcode,
405+ .size_in = cmd->info.size_in,
406+ .size_out = cmd->info.size_out,
407+ };
408+ int rc;
409+
410+ if (cmd->info.size_out) {
411+ mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL);
412+ if (!mbox_cmd.payload_out)
413+ return -ENOMEM;
414+ }
415+
416+ if (cmd->info.size_in) {
417+ mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload),
418+ cmd->info.size_in);
419+ if (IS_ERR(mbox_cmd.payload_in))
420+ return PTR_ERR(mbox_cmd.payload_in);
421+ }
422+
423+ rc = cxl_mem_mbox_get(cxlm);
424+ if (rc)
425+ goto out;
426+
427+ dev_dbg(dev,
428+ "Submitting %s command for user\n"
429+ "\topcode: %x\n"
430+ "\tsize: %ub\n",
431+ cxl_command_names[cmd->info.id].name, mbox_cmd.opcode,
432+ cmd->info.size_in);
433+
434+ rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd);
435+ cxl_mem_mbox_put(cxlm);
436+ if (rc)
437+ goto out;
438+
439+ /*
440+ * @size_out contains the max size that's allowed to be written back out
441+ * to userspace. While the payload may have written more output than
442+ * this it will have to be ignored.
443+ */
444+ if (mbox_cmd.size_out) {
445+ dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out,
446+ "Invalid return size\n");
447+ if (copy_to_user(u64_to_user_ptr(out_payload),
448+ mbox_cmd.payload_out, mbox_cmd.size_out)) {
449+ rc = -EFAULT;
450+ goto out;
451+ }
452+ }
453+
454+ *size_out = mbox_cmd.size_out;
455+ *retval = mbox_cmd.return_code;
456+
457+out:
458+ kvfree(mbox_cmd.payload_in);
459+ kvfree(mbox_cmd.payload_out);
460+ return rc;
461+}
462+
463+/**
464+ * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
465+ * @cxlm: &struct cxl_mem device whose mailbox will be used.
466+ * @send_cmd: &struct cxl_send_command copied in from userspace.
467+ * @out_cmd: Sanitized and populated &struct cxl_mem_command.
468+ *
469+ * Return:
470+ * * %0 - @out_cmd is ready to send.
471+ * * %-ENOTTY - Invalid command specified.
472+ * * %-EINVAL - Reserved fields or invalid values were used.
473+ * * %-ENOMEM - Input or output buffer wasn't sized properly.
474+ *
475+ * The result of this command is a fully validated command in @out_cmd that is
476+ * safe to send to the hardware.
477+ *
478+ * See handle_mailbox_cmd_from_user()
479+ */
480+static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
481+ const struct cxl_send_command *send_cmd,
482+ struct cxl_mem_command *out_cmd)
483+{
484+ const struct cxl_command_info *info;
485+ struct cxl_mem_command *c;
486+
487+ if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
488+ return -ENOTTY;
489+
490+ /*
491+ * The user can never specify an input payload larger than what hardware
492+ * supports, but output can be arbitrarily large (simply write out as
493+ * much data as the hardware provides).
494+ */
495+ if (send_cmd->in.size > cxlm->payload_size)
496+ return -EINVAL;
497+
498+ if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
499+ return -EINVAL;
500+
501+ if (send_cmd->rsvd)
502+ return -EINVAL;
503+
504+ if (send_cmd->in.rsvd || send_cmd->out.rsvd)
505+ return -EINVAL;
506+
507+ /* Convert user's command into the internal representation */
508+ c = &mem_commands[send_cmd->id];
509+ info = &c->info;
510+
511+ /* Check the input buffer is the expected size */
512+ if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
513+ return -ENOMEM;
514+
515+ /* Check the output buffer is at least large enough */
516+ if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
517+ return -ENOMEM;
518+
519+ memcpy(out_cmd, c, sizeof(*c));
520+ out_cmd->info.size_in = send_cmd->in.size;
521+ /*
522+ * XXX: out_cmd->info.size_out will be controlled by the driver, and the
523+ * specified number of bytes @send_cmd->out.size will be copied back out
524+ * to userspace.
525+ */
526+
527+ return 0;
528+}
529+
530+static int cxl_query_cmd(struct cxl_memdev *cxlmd,
531+ struct cxl_mem_query_commands __user *q)
532+{
533+ struct device *dev = &cxlmd->dev;
534+ struct cxl_mem_command *cmd;
535+ u32 n_commands;
536+ int j = 0;
537+
538+ dev_dbg(dev, "Query IOCTL\n");
539+
540+ if (get_user(n_commands, &q->n_commands))
541+ return -EFAULT;
542+
543+ /* returns the total number if 0 elements are requested. */
544+ if (n_commands == 0)
545+ return put_user(cxl_cmd_count, &q->n_commands);
546+
547+ /*
548+ * otherwise, return max(n_commands, total commands) cxl_command_info
549+ * structures.
550+ */
551+ cxl_for_each_cmd(cmd) {
552+ const struct cxl_command_info *info = &cmd->info;
553+
554+ if (copy_to_user(&q->commands[j++], info, sizeof(*info)))
555+ return -EFAULT;
556+
557+ if (j == n_commands)
558+ break;
559+ }
560+
561+ return 0;
562+}
563+
564+static int cxl_send_cmd(struct cxl_memdev *cxlmd,
565+ struct cxl_send_command __user *s)
566+{
567+ struct cxl_mem *cxlm = cxlmd->cxlm;
568+ struct device *dev = &cxlmd->dev;
569+ struct cxl_send_command send;
570+ struct cxl_mem_command c;
571+ int rc;
572+
573+ dev_dbg(dev, "Send IOCTL\n");
574+
575+ if (copy_from_user(&send, s, sizeof(send)))
576+ return -EFAULT;
577+
578+ rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c);
579+ if (rc)
580+ return rc;
581+
582+ /* Prepare to handle a full payload for variable sized output */
583+ if (c.info.size_out < 0)
584+ c.info.size_out = cxlm->payload_size;
585+
586+ rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload,
587+ send.out.payload, &send.out.size,
588+ &send.retval);
589+ if (rc)
590+ return rc;
591+
592+ return copy_to_user(s, &send, sizeof(send));
593+}
594+
595+static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
596+ unsigned long arg)
597+{
598+ switch (cmd) {
599+ case CXL_MEM_QUERY_COMMANDS:
600+ return cxl_query_cmd(cxlmd, (void __user *)arg);
601+ case CXL_MEM_SEND_COMMAND:
602+ return cxl_send_cmd(cxlmd, (void __user *)arg);
603+ default:
604+ return -ENOTTY;
605+ }
606+}
607+
327608 static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
328609 unsigned long arg)
329610 {
@@ -337,7 +618,7 @@ static long cxl_memdev_ioctl(struct file *file, unsigned int cmd, · 7 unmodified lines
337618 if (!percpu_ref_tryget_live(&cxlmd->ops_active))
338619 return -ENXIO;
339620
340− /* TODO: ioctl body */
621+ rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
341622
342623 percpu_ref_put(&cxlmd->ops_active);
343624
include/uapi/linux/cxl_mem.h+156−0
@@ -0,0 +1,156 @@
1+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+/*
3+ * CXL IOCTLs for Memory Devices
4+ */
5+
6+#ifndef _UAPI_CXL_MEM_H_
7+#define _UAPI_CXL_MEM_H_
8+
9+#include <linux/types.h>
10+
11+/**
12+ * DOC: UAPI
13+ *
14+ * Not all of all commands that the driver supports are always available for use
15+ * by userspace. Userspace must check the results from the QUERY command in
16+ * order to determine the live set of commands.
17+ */
18+
19+#define CXL_MEM_QUERY_COMMANDS _IOR(0xCE, 1, struct cxl_mem_query_commands)
20+#define CXL_MEM_SEND_COMMAND _IOWR(0xCE, 2, struct cxl_send_command)
21+
22+#define CXL_CMDS \
23+ ___C(INVALID, "Invalid Command"), \
24+ ___C(IDENTIFY, "Identify Command"), \
25+ ___C(MAX, "invalid / last command")
26+
27+#define ___C(a, b) CXL_MEM_COMMAND_ID_##a
28+enum { CXL_CMDS };
29+
30+#undef ___C
31+#define ___C(a, b) { b }
32+static const struct {
33+ const char *name;
34+} cxl_command_names[] = { CXL_CMDS };
35+
36+/*
37+ * Here's how this actually breaks out:
38+ * cxl_command_names[] = {
39+ * [CXL_MEM_COMMAND_ID_INVALID] = { "Invalid Command" },
40+ * [CXL_MEM_COMMAND_ID_IDENTIFY] = { "Identify Command" },
41+ * ...
42+ * [CXL_MEM_COMMAND_ID_MAX] = { "invalid / last command" },
43+ * };
44+ */
45+
46+#undef ___C
47+
48+/**
49+ * struct cxl_command_info - Command information returned from a query.
50+ * @id: ID number for the command.
51+ * @flags: Flags that specify command behavior.
52+ * @size_in: Expected input size, or -1 if variable length.
53+ * @size_out: Expected output size, or -1 if variable length.
54+ *
55+ * Represents a single command that is supported by both the driver and the
56+ * hardware. This is returned as part of an array from the query ioctl. The
57+ * following would be a command that takes a variable length input and returns 0
58+ * bytes of output.
59+ *
60+ * - @id = 10
61+ * - @flags = 0
62+ * - @size_in = -1
63+ * - @size_out = 0
64+ *
65+ * See struct cxl_mem_query_commands.
66+ */
67+struct cxl_command_info {
68+ __u32 id;
69+
70+ __u32 flags;
71+#define CXL_MEM_COMMAND_FLAG_MASK GENMASK(0, 0)
72+
73+ __s32 size_in;
74+ __s32 size_out;
75+};
76+
77+/**
78+ * struct cxl_mem_query_commands - Query supported commands.
79+ * @n_commands: In/out parameter. When @n_commands is > 0, the driver will
80+ * return min(num_support_commands, n_commands). When @n_commands
81+ * is 0, driver will return the number of total supported commands.
82+ * @rsvd: Reserved for future use.
83+ * @commands: Output array of supported commands. This array must be allocated
84+ * by userspace to be at least min(num_support_commands, @n_commands)
85+ *
86+ * Allow userspace to query the available commands supported by both the driver,
87+ * and the hardware. Commands that aren't supported by either the driver, or the
88+ * hardware are not returned in the query.
89+ *
90+ * Examples:
91+ *
92+ * - { .n_commands = 0 } // Get number of supported commands
93+ * - { .n_commands = 15, .commands = buf } // Return first 15 (or less)
94+ * supported commands
95+ *
96+ * See struct cxl_command_info.
97+ */
98+struct cxl_mem_query_commands {
99+ /*
100+ * Input: Number of commands to return (space allocated by user)
101+ * Output: Number of commands supported by the driver/hardware
102+ *
103+ * If n_commands is 0, kernel will only return number of commands and
104+ * not try to populate commands[], thus allowing userspace to know how
105+ * much space to allocate
106+ */
107+ __u32 n_commands;
108+ __u32 rsvd;
109+
110+ struct cxl_command_info __user commands[]; /* out: supported commands */
111+};
112+
113+/**
114+ * struct cxl_send_command - Send a command to a memory device.
115+ * @id: The command to send to the memory device. This must be one of the
116+ * commands returned by the query command.
117+ * @flags: Flags for the command (input).
118+ * @rsvd: Must be zero.
119+ * @retval: Return value from the memory device (output).
120+ * @in: Parameters associated with input payload.
121+ * @in.size: Size of the payload to provide to the device (input).
122+ * @in.rsvd: Must be zero.
123+ * @in.payload: Pointer to memory for payload input, payload is little endian.
124+ * @out: Parameters associated with output payload.
125+ * @out.size: Size of the payload received from the device (input/output). This
126+ * field is filled in by userspace to let the driver know how much
127+ * space was allocated for output. It is populated by the driver to
128+ * let userspace know how large the output payload actually was.
129+ * @out.rsvd: Must be zero.
130+ * @out.payload: Pointer to memory for payload output, payload is little endian.
131+ *
132+ * Mechanism for userspace to send a command to the hardware for processing. The
133+ * driver will do basic validation on the command sizes. In some cases even the
134+ * payload may be introspected. Userspace is required to allocate large enough
135+ * buffers for size_out which can be variable length in certain situations.
136+ */
137+struct cxl_send_command {
138+ __u32 id;
139+ __u32 flags;
140+ __u32 rsvd;
141+ __u32 retval;
142+
143+ struct {
144+ __s32 size;
145+ __u32 rsvd;
146+ __u64 payload;
147+ } in;
148+
149+ struct {
150+ __s32 size;
151+ __u32 rsvd;
152+ __u64 payload;
153+ } out;
154+};
155+
156+#endif