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
109 - 'css_for_each_child'109 - 'css_for_each_child'
110 - 'css_for_each_descendant_post'110 - 'css_for_each_descendant_post'
111 - 'css_for_each_descendant_pre'111 - 'css_for_each_descendant_pre'
112 - 'cxl_for_each_cmd'
112 - 'device_for_each_child_node'113 - 'device_for_each_child_node'
113 - 'dma_fence_chain_for_each'114 - 'dma_fence_chain_for_each'
114 - 'do_for_each_ftrace_op'115 - 'do_for_each_ftrace_op'
Documentation/driver-api/cxl/memory-devices.rst+12−0
@@ -32,3 +32,15 @@ CXL Bus · 31 unmodified lines
32-------32-------
33.. kernel-doc:: drivers/cxl/bus.c33.. kernel-doc:: drivers/cxl/bus.c
34 :doc: cxl bus34 :doc: cxl bus
35
36External Interfaces
37===================
38
39CXL 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
352 <mailto:michael.klein@puffin.lb.shuttle.de>352 <mailto:michael.klein@puffin.lb.shuttle.de>
3530xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver3530xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver
3540xCD 01 linux/reiserfs_fs.h3540xCD 01 linux/reiserfs_fs.h
3550xCE 01-02 uapi/linux/cxl_mem.h Compute Express Link Memory Devices
3550xCF 02 fs/cifs/ioctl.c3560xCF 02 fs/cifs/ioctl.c
3560xDB 00-0F drivers/char/mwave/mwavepub.h3570xDB 00-0F drivers/char/mwave/mwavepub.h
3570xDD 00-3F ZFCP device driver see drivers/s390/scsi/3580xDD 00-3F ZFCP device driver see drivers/s390/scsi/
drivers/cxl/mem.c+282−1
@@ -1,5 +1,6 @@
1// SPDX-License-Identifier: GPL-2.0-only1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2020 Intel Corporation. All rights reserved. */2/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
3#include <uapi/linux/cxl_mem.h>
3#include <linux/module.h>4#include <linux/module.h>
4#include <linux/mutex.h>5#include <linux/mutex.h>
5#include <linux/cdev.h>6#include <linux/cdev.h>
@@ -40,6 +41,7 @@ · 34 unmodified lines
40#define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)41#define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
41 42
42enum opcode {43enum opcode {
44 CXL_MBOX_OP_INVALID = 0x0000,
43 CXL_MBOX_OP_IDENTIFY = 0x4000,45 CXL_MBOX_OP_IDENTIFY = 0x4000,
44 CXL_MBOX_OP_MAX = 0x1000046 CXL_MBOX_OP_MAX = 0x10000
45};47};
@@ -95,6 +97,49 @@ struct cxl_memdev { · 49 unmodified lines
95static int cxl_mem_major;97static int cxl_mem_major;
96static DEFINE_IDA(cxl_memdev_ida);98static DEFINE_IDA(cxl_memdev_ida);
97 99
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 */
112struct 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 */
133static 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
98static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)143static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
99{144{
100 const unsigned long start = jiffies;145 const unsigned long start = jiffies;
@@ -324,6 +369,242 @@ static void cxl_mem_mbox_put(struct cxl_mem *cxlm) · 223 unmodified lines
324 mutex_unlock(&cxlm->mbox_mutex);369 mutex_unlock(&cxlm->mbox_mutex);
325}370}
326 371
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 */
397static 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
457out:
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 */
480static 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
530static 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
564static 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
595static 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
327static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,608static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
328 unsigned long arg)609 unsigned long arg)
329{610{
@@ -337,7 +618,7 @@ static long cxl_memdev_ioctl(struct file *file, unsigned int cmd, · 7 unmodified lines
337 if (!percpu_ref_tryget_live(&cxlmd->ops_active))618 if (!percpu_ref_tryget_live(&cxlmd->ops_active))
338 return -ENXIO;619 return -ENXIO;
339 620
340 /* TODO: ioctl body */621 rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
341 622
342 percpu_ref_put(&cxlmd->ops_active);623 percpu_ref_put(&cxlmd->ops_active);
343 624
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
28enum { CXL_CMDS };
29
30#undef ___C
31#define ___C(a, b) { b }
32static 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 */
67struct 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 */
98struct 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 */
137struct 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