diff mbox series

[v2,1/4] cxl/mbox: Move cxl_mem_command construction to helper funcs

Message ID 84c0ddda79331bbbc37a6237e980b53735c29c77.1645817416.git.alison.schofield@intel.com
State Superseded
Headers show
Series Do not allow set-partition immediate mode | expand

Commit Message

Alison Schofield Feb. 25, 2022, 8:30 p.m. UTC
From: Alison Schofield <alison.schofield@intel.com>

Sanitizing and constructing a cxl_mem_command from a userspace
command is part of the validation process prior to submitting
the command to a CXL device. Move this work to helper functions:
cxl_to_mem_cmd(), cxl_to_mem_cmd_raw().

This declutters cxl_validate_cmd_from_user() in preparation for
adding new validation steps.

Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
 drivers/cxl/core/mbox.c | 143 ++++++++++++++++++++++------------------
 1 file changed, 79 insertions(+), 64 deletions(-)

Comments

Dan Williams Feb. 25, 2022, 9:31 p.m. UTC | #1
On Fri, Feb 25, 2022 at 12:27 PM <alison.schofield@intel.com> wrote:
>
> From: Alison Schofield <alison.schofield@intel.com>
>
> Sanitizing and constructing a cxl_mem_command from a userspace
> command is part of the validation process prior to submitting
> the command to a CXL device. Move this work to helper functions:
> cxl_to_mem_cmd(), cxl_to_mem_cmd_raw().
>
> This declutters cxl_validate_cmd_from_user() in preparation for
> adding new validation steps.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> ---
>  drivers/cxl/core/mbox.c | 143 ++++++++++++++++++++++------------------
>  1 file changed, 79 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index be61a0d8016b..06fbe6d079ba 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -207,6 +207,75 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
>         return true;
>  }
>
> +static int cxl_to_mem_cmd_raw(struct cxl_dev_state *cxlds,
> +                             const struct cxl_send_command *send_cmd,
> +                             struct cxl_mem_command *mem_cmd)
> +{
> +       if (send_cmd->raw.rsvd)
> +               return -EINVAL;
> +       /*
> +        * Unlike supported commands, the output size of RAW commands
> +        * gets passed along without further checking, so it must be
> +        * validated here.
> +        */
> +       if (send_cmd->out.size > cxlds->payload_size)
> +               return -EINVAL;
> +
> +       if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
> +               return -EPERM;
> +
> +       dev_WARN_ONCE(cxlds->dev, true, "raw command path used\n");
> +
> +       mem_cmd->info.id = CXL_MEM_COMMAND_ID_RAW;
> +       mem_cmd->info.flags = 0;
> +       mem_cmd->info.size_in = send_cmd->in.size;
> +       mem_cmd->info.size_out = send_cmd->out.size;
> +       mem_cmd->opcode = send_cmd->raw.opcode;

One subtle change from the previous version to this one is that it no
longer gets automatic zero initialization by the compiler. This leaves
a subtle timebomb if 'struct cxl_mem_command' is ever updated. So I
would write this as:

                *mem_cmd = (struct cxl_mem_command) {
                        .info = {
                                .id = CXL_MEM_COMMAND_ID_RAW,
                                .size_in = send_cmd->in.size,
                                .size_out = send_cmd->out.size,
                        },
                        .opcode = send_cmd->raw.opcode
                };

...where you can skip setting flags to 0 since that is implied by the
syntax and there is no need to worry about if all the unwritten fields
are zeroed.

> +
> +       return 0;
> +}
> +
> +static int cxl_to_mem_cmd(struct cxl_dev_state *cxlds,
> +                         const struct cxl_send_command *send_cmd,
> +                         struct cxl_mem_command *mem_cmd)
> +{
> +       const struct cxl_command_info *info;
> +       struct cxl_mem_command *c;
> +
> +       if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
> +               return -EINVAL;
> +
> +       if (send_cmd->rsvd)
> +               return -EINVAL;
> +
> +       if (send_cmd->in.rsvd || send_cmd->out.rsvd)
> +               return -EINVAL;
> +
> +       /* Convert user's command into the internal representation */
> +       c = &cxl_mem_commands[send_cmd->id];
> +       info = &c->info;
> +
> +       /* Check that the command is enabled for hardware */
> +       if (!test_bit(info->id, cxlds->enabled_cmds))
> +               return -ENOTTY;
> +
> +       /* Check that the command is not claimed for exclusive kernel use */
> +       if (test_bit(info->id, cxlds->exclusive_cmds))
> +               return -EBUSY;
> +
> +       /* Check the input buffer is the expected size */
> +       if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
> +               return -ENOMEM;
> +
> +       /* Check the output buffer is at least large enough */
> +       if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
> +               return -ENOMEM;
> +
> +       memcpy(mem_cmd, c, sizeof(*c));
> +       mem_cmd->info.size_in = send_cmd->in.size;

Perhaps for symmetry this can become:

                *mem_cmd = (struct cxl_mem_command) {
                        .info = {
                                .id = info->id,
                                .flags = info->flags,
                                .size_in = send_cmd->in.size,
                                .size_out = info->size_out,
                        },
                        .opcode = c->opcode
                };

...if I got that right...

> +       return 0;
> +}
> +
>  /**
>   * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
>   * @cxlds: The device data for the operation
> @@ -230,8 +299,8 @@ static int cxl_validate_cmd_from_user(struct cxl_dev_state *cxlds,
>                                       const struct cxl_send_command *send_cmd,
>                                       struct cxl_mem_command *out_cmd)
>  {
> -       const struct cxl_command_info *info;
> -       struct cxl_mem_command *c;
> +       struct cxl_mem_command mem_cmd;
> +       int rc;
>
>         if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
>                 return -ENOTTY;
> @@ -244,70 +313,16 @@ static int cxl_validate_cmd_from_user(struct cxl_dev_state *cxlds,
>         if (send_cmd->in.size > cxlds->payload_size)
>                 return -EINVAL;
>
> -       /*
> -        * Checks are bypassed for raw commands but a WARN/taint will occur
> -        * later in the callchain
> -        */
> -       if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) {
> -               const struct cxl_mem_command temp = {
> -                       .info = {
> -                               .id = CXL_MEM_COMMAND_ID_RAW,
> -                               .flags = 0,
> -                               .size_in = send_cmd->in.size,
> -                               .size_out = send_cmd->out.size,
> -                       },
> -                       .opcode = send_cmd->raw.opcode
> -               };
> +       /* Sanitize and construct a cxl_mem_command */
> +       if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW)
> +               rc = cxl_to_mem_cmd_raw(cxlds, send_cmd, &mem_cmd);
> +       else
> +               rc = cxl_to_mem_cmd(cxlds, send_cmd, &mem_cmd);
>
> -               if (send_cmd->raw.rsvd)
> -                       return -EINVAL;
> +       if (rc)
> +               return rc;
>
> -               /*
> -                * Unlike supported commands, the output size of RAW commands
> -                * gets passed along without further checking, so it must be
> -                * validated here.
> -                */
> -               if (send_cmd->out.size > cxlds->payload_size)
> -                       return -EINVAL;
> -
> -               if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
> -                       return -EPERM;
> -
> -               memcpy(out_cmd, &temp, sizeof(temp));
> -
> -               return 0;
> -       }
> -
> -       if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
> -               return -EINVAL;
> -
> -       if (send_cmd->rsvd)
> -               return -EINVAL;
> -
> -       if (send_cmd->in.rsvd || send_cmd->out.rsvd)
> -               return -EINVAL;
> -
> -       /* Convert user's command into the internal representation */
> -       c = &cxl_mem_commands[send_cmd->id];
> -       info = &c->info;
> -
> -       /* Check that the command is enabled for hardware */
> -       if (!test_bit(info->id, cxlds->enabled_cmds))
> -               return -ENOTTY;
> -
> -       /* Check that the command is not claimed for exclusive kernel use */
> -       if (test_bit(info->id, cxlds->exclusive_cmds))
> -               return -EBUSY;
> -
> -       /* Check the input buffer is the expected size */
> -       if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
> -               return -ENOMEM;
> -
> -       /* Check the output buffer is at least large enough */
> -       if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
> -               return -ENOMEM;
> -
> -       memcpy(out_cmd, c, sizeof(*c));
> +       memcpy(out_cmd, &mem_cmd, sizeof(mem_cmd));

Given the above helpers are already copying into a pointer, why not
pass them out_cmd directly instead of the extra mem_cmd hop?

>         out_cmd->info.size_in = send_cmd->in.size;

This could also be deleted since both helpers above already set
size_in = send_cmd->in.size.
diff mbox series

Patch

diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index be61a0d8016b..06fbe6d079ba 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -207,6 +207,75 @@  static bool cxl_mem_raw_command_allowed(u16 opcode)
 	return true;
 }
 
+static int cxl_to_mem_cmd_raw(struct cxl_dev_state *cxlds,
+			      const struct cxl_send_command *send_cmd,
+			      struct cxl_mem_command *mem_cmd)
+{
+	if (send_cmd->raw.rsvd)
+		return -EINVAL;
+	/*
+	 * Unlike supported commands, the output size of RAW commands
+	 * gets passed along without further checking, so it must be
+	 * validated here.
+	 */
+	if (send_cmd->out.size > cxlds->payload_size)
+		return -EINVAL;
+
+	if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
+		return -EPERM;
+
+	dev_WARN_ONCE(cxlds->dev, true, "raw command path used\n");
+
+	mem_cmd->info.id = CXL_MEM_COMMAND_ID_RAW;
+	mem_cmd->info.flags = 0;
+	mem_cmd->info.size_in = send_cmd->in.size;
+	mem_cmd->info.size_out = send_cmd->out.size;
+	mem_cmd->opcode = send_cmd->raw.opcode;
+
+	return 0;
+}
+
+static int cxl_to_mem_cmd(struct cxl_dev_state *cxlds,
+			  const struct cxl_send_command *send_cmd,
+			  struct cxl_mem_command *mem_cmd)
+{
+	const struct cxl_command_info *info;
+	struct cxl_mem_command *c;
+
+	if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
+		return -EINVAL;
+
+	if (send_cmd->rsvd)
+		return -EINVAL;
+
+	if (send_cmd->in.rsvd || send_cmd->out.rsvd)
+		return -EINVAL;
+
+	/* Convert user's command into the internal representation */
+	c = &cxl_mem_commands[send_cmd->id];
+	info = &c->info;
+
+	/* Check that the command is enabled for hardware */
+	if (!test_bit(info->id, cxlds->enabled_cmds))
+		return -ENOTTY;
+
+	/* Check that the command is not claimed for exclusive kernel use */
+	if (test_bit(info->id, cxlds->exclusive_cmds))
+		return -EBUSY;
+
+	/* Check the input buffer is the expected size */
+	if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
+		return -ENOMEM;
+
+	/* Check the output buffer is at least large enough */
+	if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
+		return -ENOMEM;
+
+	memcpy(mem_cmd, c, sizeof(*c));
+	mem_cmd->info.size_in = send_cmd->in.size;
+	return 0;
+}
+
 /**
  * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
  * @cxlds: The device data for the operation
@@ -230,8 +299,8 @@  static int cxl_validate_cmd_from_user(struct cxl_dev_state *cxlds,
 				      const struct cxl_send_command *send_cmd,
 				      struct cxl_mem_command *out_cmd)
 {
-	const struct cxl_command_info *info;
-	struct cxl_mem_command *c;
+	struct cxl_mem_command mem_cmd;
+	int rc;
 
 	if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
 		return -ENOTTY;
@@ -244,70 +313,16 @@  static int cxl_validate_cmd_from_user(struct cxl_dev_state *cxlds,
 	if (send_cmd->in.size > cxlds->payload_size)
 		return -EINVAL;
 
-	/*
-	 * Checks are bypassed for raw commands but a WARN/taint will occur
-	 * later in the callchain
-	 */
-	if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) {
-		const struct cxl_mem_command temp = {
-			.info = {
-				.id = CXL_MEM_COMMAND_ID_RAW,
-				.flags = 0,
-				.size_in = send_cmd->in.size,
-				.size_out = send_cmd->out.size,
-			},
-			.opcode = send_cmd->raw.opcode
-		};
+	/* Sanitize and construct a cxl_mem_command */
+	if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW)
+		rc = cxl_to_mem_cmd_raw(cxlds, send_cmd, &mem_cmd);
+	else
+		rc = cxl_to_mem_cmd(cxlds, send_cmd, &mem_cmd);
 
-		if (send_cmd->raw.rsvd)
-			return -EINVAL;
+	if (rc)
+		return rc;
 
-		/*
-		 * Unlike supported commands, the output size of RAW commands
-		 * gets passed along without further checking, so it must be
-		 * validated here.
-		 */
-		if (send_cmd->out.size > cxlds->payload_size)
-			return -EINVAL;
-
-		if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
-			return -EPERM;
-
-		memcpy(out_cmd, &temp, sizeof(temp));
-
-		return 0;
-	}
-
-	if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
-		return -EINVAL;
-
-	if (send_cmd->rsvd)
-		return -EINVAL;
-
-	if (send_cmd->in.rsvd || send_cmd->out.rsvd)
-		return -EINVAL;
-
-	/* Convert user's command into the internal representation */
-	c = &cxl_mem_commands[send_cmd->id];
-	info = &c->info;
-
-	/* Check that the command is enabled for hardware */
-	if (!test_bit(info->id, cxlds->enabled_cmds))
-		return -ENOTTY;
-
-	/* Check that the command is not claimed for exclusive kernel use */
-	if (test_bit(info->id, cxlds->exclusive_cmds))
-		return -EBUSY;
-
-	/* Check the input buffer is the expected size */
-	if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
-		return -ENOMEM;
-
-	/* Check the output buffer is at least large enough */
-	if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
-		return -ENOMEM;
-
-	memcpy(out_cmd, c, sizeof(*c));
+	memcpy(out_cmd, &mem_cmd, sizeof(mem_cmd));
 	out_cmd->info.size_in = send_cmd->in.size;
 	/*
 	 * XXX: out_cmd->info.size_out will be controlled by the driver, and the