Commit aed968f8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull cxl fixes from Dave Jiang:

 - Fix incorrect usages of decoder flags

 - Validate payload size before accessing contents

 - Fix race condition when creating nvdimm objects

 - Fix deadlock on attach failure

* tag 'cxl-fixes-7.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl:
  cxl/region: Test CXL_DECODER_F_NORMALIZED_ADDRESSING as a bitmask
  cxl: Test CXL_DECODER_F_LOCK as a bitmask
  cxl/mbox: validate payload size before accessing contents in cxl_payload_from_user_allowed()
  cxl: Fix race of nvdimm_bus object when creating nvdimm objects
  cxl: Move devm_cxl_add_nvdimm_bridge() to cxl_pmem.ko
  cxl/port: Hold port host lock during dport adding.
  cxl/port: Introduce port_to_host() helper
  cxl/memdev: fix deadlock in cxl_memdev_autoremove() on attach failure
parents 962336b9 e46f25f5
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -152,6 +152,24 @@ int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c);
int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port,
					struct access_coordinate *c);

static inline struct device *port_to_host(struct cxl_port *port)
{
	struct cxl_port *parent = is_cxl_root(port) ? NULL :
				  to_cxl_port(port->dev.parent);

	/*
	 * The host of CXL root port and the first level of ports is
	 * the platform firmware device, the host of all other ports
	 * is their parent port.
	 */
	if (!parent)
		return port->uport_dev;
	else if (is_cxl_root(parent))
		return parent->uport_dev;
	else
		return &parent->dev;
}

static inline struct device *dport_to_host(struct cxl_dport *dport)
{
	struct cxl_port *port = dport->port;
+1 −1
Original line number Diff line number Diff line
@@ -904,7 +904,7 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
	if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
		return;

	if (test_bit(CXL_DECODER_F_LOCK, &cxld->flags))
	if (cxld->flags & CXL_DECODER_F_LOCK)
		return;

	if (port->commit_end == id)
+9 −2
Original line number Diff line number Diff line
@@ -311,6 +311,7 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
 * cxl_payload_from_user_allowed() - Check contents of in_payload.
 * @opcode: The mailbox command opcode.
 * @payload_in: Pointer to the input payload passed in from user space.
 * @in_size: Size of @payload_in in bytes.
 *
 * Return:
 *  * true	- payload_in passes check for @opcode.
@@ -325,12 +326,15 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
 *
 * The specific checks are determined by the opcode.
 */
static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in)
static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in,
					  size_t in_size)
{
	switch (opcode) {
	case CXL_MBOX_OP_SET_PARTITION_INFO: {
		struct cxl_mbox_set_partition_info *pi = payload_in;

		if (in_size < sizeof(*pi))
			return false;
		if (pi->flags & CXL_SET_PARTITION_IMMEDIATE_FLAG)
			return false;
		break;
@@ -338,6 +342,8 @@ static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in)
	case CXL_MBOX_OP_CLEAR_LOG: {
		const uuid_t *uuid = (uuid_t *)payload_in;

		if (in_size < sizeof(uuid_t))
			return false;
		/*
		 * Restrict the ‘Clear log’ action to only apply to
		 * Vendor debug logs.
@@ -365,7 +371,8 @@ static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox_cmd,
		if (IS_ERR(mbox_cmd->payload_in))
			return PTR_ERR(mbox_cmd->payload_in);

		if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in)) {
		if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in,
						  in_size)) {
			dev_dbg(cxl_mbox->host, "%s: input payload not allowed\n",
				cxl_mem_opcode_to_name(opcode));
			kvfree(mbox_cmd->payload_in);
+9 −4
Original line number Diff line number Diff line
@@ -1089,10 +1089,8 @@ static int cxlmd_add(struct cxl_memdev *cxlmd, struct cxl_dev_state *cxlds)
DEFINE_FREE(put_cxlmd, struct cxl_memdev *,
	    if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))

static struct cxl_memdev *cxl_memdev_autoremove(struct cxl_memdev *cxlmd)
static bool cxl_memdev_attach_failed(struct cxl_memdev *cxlmd)
{
	int rc;

	/*
	 * If @attach is provided fail if the driver is not attached upon
	 * return. Note that failure here could be the result of a race to
@@ -1100,7 +1098,14 @@ static struct cxl_memdev *cxl_memdev_autoremove(struct cxl_memdev *cxlmd)
	 * succeeded and then cxl_mem unbound before the lock is acquired.
	 */
	guard(device)(&cxlmd->dev);
	if (cxlmd->attach && !cxlmd->dev.driver) {
	return (cxlmd->attach && !cxlmd->dev.driver);
}

static struct cxl_memdev *cxl_memdev_autoremove(struct cxl_memdev *cxlmd)
{
	int rc;

	if (cxl_memdev_attach_failed(cxlmd)) {
		cxl_memdev_unregister(cxlmd);
		return ERR_PTR(-ENXIO);
	}
+32 −10
Original line number Diff line number Diff line
@@ -115,14 +115,16 @@ static void unregister_nvb(void *_cxl_nvb)
	device_unregister(&cxl_nvb->dev);
}

/**
 * devm_cxl_add_nvdimm_bridge() - add the root of a LIBNVDIMM topology
 * @host: platform firmware root device
 * @port: CXL port at the root of a CXL topology
 *
 * Return: bridge device that can host cxl_nvdimm objects
 */
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
static bool cxl_nvdimm_bridge_failed_attach(struct cxl_nvdimm_bridge *cxl_nvb)
{
	struct device *dev = &cxl_nvb->dev;

	guard(device)(dev);
	/* If the device has no driver, then it failed to attach. */
	return dev->driver == NULL;
}

struct cxl_nvdimm_bridge *__devm_cxl_add_nvdimm_bridge(struct device *host,
						       struct cxl_port *port)
{
	struct cxl_nvdimm_bridge *cxl_nvb;
@@ -145,6 +147,11 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
	if (rc)
		goto err;

	if (cxl_nvdimm_bridge_failed_attach(cxl_nvb)) {
		unregister_nvb(cxl_nvb);
		return ERR_PTR(-ENODEV);
	}

	rc = devm_add_action_or_reset(host, unregister_nvb, cxl_nvb);
	if (rc)
		return ERR_PTR(rc);
@@ -155,7 +162,7 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
	put_device(dev);
	return ERR_PTR(rc);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, "CXL");
EXPORT_SYMBOL_FOR_MODULES(__devm_cxl_add_nvdimm_bridge, "cxl_pmem");

static void cxl_nvdimm_release(struct device *dev)
{
@@ -255,6 +262,21 @@ int devm_cxl_add_nvdimm(struct device *host, struct cxl_port *port,
	if (!cxl_nvb)
		return -ENODEV;

	/*
	 * Take the uport_dev lock to guard against race of nvdimm_bus object.
	 * cxl_acpi_probe() registers the nvdimm_bus and is done under the
	 * root port uport_dev lock.
	 *
	 * Take the cxl_nvb device lock to ensure that cxl_nvb driver is in a
	 * consistent state. And the driver registers nvdimm_bus.
	 */
	guard(device)(cxl_nvb->port->uport_dev);
	guard(device)(&cxl_nvb->dev);
	if (!cxl_nvb->nvdimm_bus) {
		rc = -ENODEV;
		goto err_alloc;
	}

	cxl_nvd = cxl_nvdimm_alloc(cxl_nvb, cxlmd);
	if (IS_ERR(cxl_nvd)) {
		rc = PTR_ERR(cxl_nvd);
Loading