Skip to content
/ linux Public

Commit aed968f

Browse files
committed
Merge tag 'cxl-fixes-7.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl
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
2 parents 962336b + e46f25f commit aed968f

File tree

9 files changed

+117
-54
lines changed

9 files changed

+117
-54
lines changed

drivers/cxl/core/core.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,24 @@ int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c);
152152
int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port,
153153
struct access_coordinate *c);
154154

155+
static inline struct device *port_to_host(struct cxl_port *port)
156+
{
157+
struct cxl_port *parent = is_cxl_root(port) ? NULL :
158+
to_cxl_port(port->dev.parent);
159+
160+
/*
161+
* The host of CXL root port and the first level of ports is
162+
* the platform firmware device, the host of all other ports
163+
* is their parent port.
164+
*/
165+
if (!parent)
166+
return port->uport_dev;
167+
else if (is_cxl_root(parent))
168+
return parent->uport_dev;
169+
else
170+
return &parent->dev;
171+
}
172+
155173
static inline struct device *dport_to_host(struct cxl_dport *dport)
156174
{
157175
struct cxl_port *port = dport->port;

drivers/cxl/core/hdm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
904904
if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
905905
return;
906906

907-
if (test_bit(CXL_DECODER_F_LOCK, &cxld->flags))
907+
if (cxld->flags & CXL_DECODER_F_LOCK)
908908
return;
909909

910910
if (port->commit_end == id)

drivers/cxl/core/mbox.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
311311
* cxl_payload_from_user_allowed() - Check contents of in_payload.
312312
* @opcode: The mailbox command opcode.
313313
* @payload_in: Pointer to the input payload passed in from user space.
314+
* @in_size: Size of @payload_in in bytes.
314315
*
315316
* Return:
316317
* * true - payload_in passes check for @opcode.
@@ -325,19 +326,24 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
325326
*
326327
* The specific checks are determined by the opcode.
327328
*/
328-
static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in)
329+
static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in,
330+
size_t in_size)
329331
{
330332
switch (opcode) {
331333
case CXL_MBOX_OP_SET_PARTITION_INFO: {
332334
struct cxl_mbox_set_partition_info *pi = payload_in;
333335

336+
if (in_size < sizeof(*pi))
337+
return false;
334338
if (pi->flags & CXL_SET_PARTITION_IMMEDIATE_FLAG)
335339
return false;
336340
break;
337341
}
338342
case CXL_MBOX_OP_CLEAR_LOG: {
339343
const uuid_t *uuid = (uuid_t *)payload_in;
340344

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

368-
if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in)) {
374+
if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in,
375+
in_size)) {
369376
dev_dbg(cxl_mbox->host, "%s: input payload not allowed\n",
370377
cxl_mem_opcode_to_name(opcode));
371378
kvfree(mbox_cmd->payload_in);

drivers/cxl/core/memdev.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,18 +1089,23 @@ static int cxlmd_add(struct cxl_memdev *cxlmd, struct cxl_dev_state *cxlds)
10891089
DEFINE_FREE(put_cxlmd, struct cxl_memdev *,
10901090
if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
10911091

1092-
static struct cxl_memdev *cxl_memdev_autoremove(struct cxl_memdev *cxlmd)
1092+
static bool cxl_memdev_attach_failed(struct cxl_memdev *cxlmd)
10931093
{
1094-
int rc;
1095-
10961094
/*
10971095
* If @attach is provided fail if the driver is not attached upon
10981096
* return. Note that failure here could be the result of a race to
10991097
* teardown the CXL port topology. I.e. cxl_mem_probe() could have
11001098
* succeeded and then cxl_mem unbound before the lock is acquired.
11011099
*/
11021100
guard(device)(&cxlmd->dev);
1103-
if (cxlmd->attach && !cxlmd->dev.driver) {
1101+
return (cxlmd->attach && !cxlmd->dev.driver);
1102+
}
1103+
1104+
static struct cxl_memdev *cxl_memdev_autoremove(struct cxl_memdev *cxlmd)
1105+
{
1106+
int rc;
1107+
1108+
if (cxl_memdev_attach_failed(cxlmd)) {
11041109
cxl_memdev_unregister(cxlmd);
11051110
return ERR_PTR(-ENXIO);
11061111
}

drivers/cxl/core/pmem.c

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,17 @@ static void unregister_nvb(void *_cxl_nvb)
115115
device_unregister(&cxl_nvb->dev);
116116
}
117117

118-
/**
119-
* devm_cxl_add_nvdimm_bridge() - add the root of a LIBNVDIMM topology
120-
* @host: platform firmware root device
121-
* @port: CXL port at the root of a CXL topology
122-
*
123-
* Return: bridge device that can host cxl_nvdimm objects
124-
*/
125-
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
126-
struct cxl_port *port)
118+
static bool cxl_nvdimm_bridge_failed_attach(struct cxl_nvdimm_bridge *cxl_nvb)
119+
{
120+
struct device *dev = &cxl_nvb->dev;
121+
122+
guard(device)(dev);
123+
/* If the device has no driver, then it failed to attach. */
124+
return dev->driver == NULL;
125+
}
126+
127+
struct cxl_nvdimm_bridge *__devm_cxl_add_nvdimm_bridge(struct device *host,
128+
struct cxl_port *port)
127129
{
128130
struct cxl_nvdimm_bridge *cxl_nvb;
129131
struct device *dev;
@@ -145,6 +147,11 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
145147
if (rc)
146148
goto err;
147149

150+
if (cxl_nvdimm_bridge_failed_attach(cxl_nvb)) {
151+
unregister_nvb(cxl_nvb);
152+
return ERR_PTR(-ENODEV);
153+
}
154+
148155
rc = devm_add_action_or_reset(host, unregister_nvb, cxl_nvb);
149156
if (rc)
150157
return ERR_PTR(rc);
@@ -155,7 +162,7 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
155162
put_device(dev);
156163
return ERR_PTR(rc);
157164
}
158-
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, "CXL");
165+
EXPORT_SYMBOL_FOR_MODULES(__devm_cxl_add_nvdimm_bridge, "cxl_pmem");
159166

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

265+
/*
266+
* Take the uport_dev lock to guard against race of nvdimm_bus object.
267+
* cxl_acpi_probe() registers the nvdimm_bus and is done under the
268+
* root port uport_dev lock.
269+
*
270+
* Take the cxl_nvb device lock to ensure that cxl_nvb driver is in a
271+
* consistent state. And the driver registers nvdimm_bus.
272+
*/
273+
guard(device)(cxl_nvb->port->uport_dev);
274+
guard(device)(&cxl_nvb->dev);
275+
if (!cxl_nvb->nvdimm_bus) {
276+
rc = -ENODEV;
277+
goto err_alloc;
278+
}
279+
258280
cxl_nvd = cxl_nvdimm_alloc(cxl_nvb, cxlmd);
259281
if (IS_ERR(cxl_nvd)) {
260282
rc = PTR_ERR(cxl_nvd);

drivers/cxl/core/port.c

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -615,22 +615,8 @@ struct cxl_port *parent_port_of(struct cxl_port *port)
615615
static void unregister_port(void *_port)
616616
{
617617
struct cxl_port *port = _port;
618-
struct cxl_port *parent = parent_port_of(port);
619-
struct device *lock_dev;
620618

621-
/*
622-
* CXL root port's and the first level of ports are unregistered
623-
* under the platform firmware device lock, all other ports are
624-
* unregistered while holding their parent port lock.
625-
*/
626-
if (!parent)
627-
lock_dev = port->uport_dev;
628-
else if (is_cxl_root(parent))
629-
lock_dev = parent->uport_dev;
630-
else
631-
lock_dev = &parent->dev;
632-
633-
device_lock_assert(lock_dev);
619+
device_lock_assert(port_to_host(port));
634620
port->dead = true;
635621
device_unregister(&port->dev);
636622
}
@@ -1427,20 +1413,11 @@ static struct device *grandparent(struct device *dev)
14271413
return NULL;
14281414
}
14291415

1430-
static struct device *endpoint_host(struct cxl_port *endpoint)
1431-
{
1432-
struct cxl_port *port = to_cxl_port(endpoint->dev.parent);
1433-
1434-
if (is_cxl_root(port))
1435-
return port->uport_dev;
1436-
return &port->dev;
1437-
}
1438-
14391416
static void delete_endpoint(void *data)
14401417
{
14411418
struct cxl_memdev *cxlmd = data;
14421419
struct cxl_port *endpoint = cxlmd->endpoint;
1443-
struct device *host = endpoint_host(endpoint);
1420+
struct device *host = port_to_host(endpoint);
14441421

14451422
scoped_guard(device, host) {
14461423
if (host->driver && !endpoint->dead) {
@@ -1456,7 +1433,7 @@ static void delete_endpoint(void *data)
14561433

14571434
int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint)
14581435
{
1459-
struct device *host = endpoint_host(endpoint);
1436+
struct device *host = port_to_host(endpoint);
14601437
struct device *dev = &cxlmd->dev;
14611438

14621439
get_device(host);
@@ -1790,7 +1767,16 @@ static struct cxl_dport *find_or_add_dport(struct cxl_port *port,
17901767
{
17911768
struct cxl_dport *dport;
17921769

1793-
device_lock_assert(&port->dev);
1770+
/*
1771+
* The port is already visible in CXL hierarchy, but it may still
1772+
* be in the process of binding to the CXL port driver at this point.
1773+
*
1774+
* port creation and driver binding are protected by the port's host
1775+
* lock, so acquire the host lock here to ensure the port has completed
1776+
* driver binding before proceeding with dport addition.
1777+
*/
1778+
guard(device)(port_to_host(port));
1779+
guard(device)(&port->dev);
17941780
dport = cxl_find_dport_by_dev(port, dport_dev);
17951781
if (!dport) {
17961782
dport = probe_dport(port, dport_dev);
@@ -1857,13 +1843,11 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
18571843
* RP port enumerated by cxl_acpi without dport will
18581844
* have the dport added here.
18591845
*/
1860-
scoped_guard(device, &port->dev) {
1861-
dport = find_or_add_dport(port, dport_dev);
1862-
if (IS_ERR(dport)) {
1863-
if (PTR_ERR(dport) == -EAGAIN)
1864-
goto retry;
1865-
return PTR_ERR(dport);
1866-
}
1846+
dport = find_or_add_dport(port, dport_dev);
1847+
if (IS_ERR(dport)) {
1848+
if (PTR_ERR(dport) == -EAGAIN)
1849+
goto retry;
1850+
return PTR_ERR(dport);
18671851
}
18681852

18691853
rc = cxl_add_ep(dport, &cxlmd->dev);

drivers/cxl/core/region.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,12 +1100,12 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr,
11001100
static void cxl_region_setup_flags(struct cxl_region *cxlr,
11011101
struct cxl_decoder *cxld)
11021102
{
1103-
if (test_bit(CXL_DECODER_F_LOCK, &cxld->flags)) {
1103+
if (cxld->flags & CXL_DECODER_F_LOCK) {
11041104
set_bit(CXL_REGION_F_LOCK, &cxlr->flags);
11051105
clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
11061106
}
11071107

1108-
if (test_bit(CXL_DECODER_F_NORMALIZED_ADDRESSING, &cxld->flags))
1108+
if (cxld->flags & CXL_DECODER_F_NORMALIZED_ADDRESSING)
11091109
set_bit(CXL_REGION_F_NORMALIZED_ADDRESSING, &cxlr->flags);
11101110
}
11111111

drivers/cxl/cxl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,11 +574,16 @@ struct cxl_nvdimm_bridge {
574574

575575
#define CXL_DEV_ID_LEN 19
576576

577+
enum {
578+
CXL_NVD_F_INVALIDATED = 0,
579+
};
580+
577581
struct cxl_nvdimm {
578582
struct device dev;
579583
struct cxl_memdev *cxlmd;
580584
u8 dev_id[CXL_DEV_ID_LEN]; /* for nvdimm, string of 'serial' */
581585
u64 dirty_shutdowns;
586+
unsigned long flags;
582587
};
583588

584589
struct cxl_pmem_region_mapping {
@@ -920,6 +925,8 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
920925
struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev);
921926
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
922927
struct cxl_port *port);
928+
struct cxl_nvdimm_bridge *__devm_cxl_add_nvdimm_bridge(struct device *host,
929+
struct cxl_port *port);
923930
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
924931
bool is_cxl_nvdimm(struct device *dev);
925932
int devm_cxl_add_nvdimm(struct device *host, struct cxl_port *port,

drivers/cxl/pmem.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313

1414
static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
1515

16+
/**
17+
* devm_cxl_add_nvdimm_bridge() - add the root of a LIBNVDIMM topology
18+
* @host: platform firmware root device
19+
* @port: CXL port at the root of a CXL topology
20+
*
21+
* Return: bridge device that can host cxl_nvdimm objects
22+
*/
23+
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
24+
struct cxl_port *port)
25+
{
26+
return __devm_cxl_add_nvdimm_bridge(host, port);
27+
}
28+
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, "CXL");
29+
1630
static void clear_exclusive(void *mds)
1731
{
1832
clear_exclusive_cxl_commands(mds, exclusive_cmds);
@@ -129,6 +143,9 @@ static int cxl_nvdimm_probe(struct device *dev)
129143
struct nvdimm *nvdimm;
130144
int rc;
131145

146+
if (test_bit(CXL_NVD_F_INVALIDATED, &cxl_nvd->flags))
147+
return -EBUSY;
148+
132149
set_exclusive_cxl_commands(mds, exclusive_cmds);
133150
rc = devm_add_action_or_reset(dev, clear_exclusive, mds);
134151
if (rc)
@@ -309,8 +326,10 @@ static int detach_nvdimm(struct device *dev, void *data)
309326
scoped_guard(device, dev) {
310327
if (dev->driver) {
311328
cxl_nvd = to_cxl_nvdimm(dev);
312-
if (cxl_nvd->cxlmd && cxl_nvd->cxlmd->cxl_nvb == data)
329+
if (cxl_nvd->cxlmd && cxl_nvd->cxlmd->cxl_nvb == data) {
313330
release = true;
331+
set_bit(CXL_NVD_F_INVALIDATED, &cxl_nvd->flags);
332+
}
314333
}
315334
}
316335
if (release)
@@ -353,6 +372,7 @@ static struct cxl_driver cxl_nvdimm_bridge_driver = {
353372
.probe = cxl_nvdimm_bridge_probe,
354373
.id = CXL_DEVICE_NVDIMM_BRIDGE,
355374
.drv = {
375+
.probe_type = PROBE_FORCE_SYNCHRONOUS,
356376
.suppress_bind_attrs = true,
357377
},
358378
};

0 commit comments

Comments
 (0)