Skip to content

Commit 3398183

Browse files
committed
cxl/memdev: Fix sanitize vs decoder setup locking
The sanitize operation is destructive and the expectation is that the device is unmapped while in progress. The current implementation does a lockless check for decoders being active, but then does nothing to prevent decoders from racing to be committed. Introduce state tracking to resolve this race. This incidentally cleans up unpriveleged userspace from triggering mmio read cycles by spinning on reading the 'security/state' attribute. Which at a minimum is a waste since the kernel state machine can cache the completion result. Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the original implementation, but an export was never required. Fixes: 0c36b6a ("cxl/mbox: Add sanitization handling machinery") Cc: Davidlohr Bueso <dave@stgolabs.net> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 5f2da19 commit 3398183

File tree

8 files changed

+90
-49
lines changed

8 files changed

+90
-49
lines changed

drivers/cxl/core/core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ resource_size_t __rcrb_to_component(struct device *dev,
7575
enum cxl_rcrb which);
7676

7777
extern struct rw_semaphore cxl_dpa_rwsem;
78+
extern struct rw_semaphore cxl_region_rwsem;
7879

7980
int cxl_memdev_init(void);
8081
void cxl_memdev_exit(void);

drivers/cxl/core/hdm.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,25 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
650650
return -EBUSY;
651651
}
652652

653+
/*
654+
* For endpoint decoders hosted on CXL memory devices that
655+
* support the sanitize operation, make sure sanitize is not in-flight.
656+
*/
657+
if (is_endpoint_decoder(&cxld->dev)) {
658+
struct cxl_endpoint_decoder *cxled =
659+
to_cxl_endpoint_decoder(&cxld->dev);
660+
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
661+
struct cxl_memdev_state *mds =
662+
to_cxl_memdev_state(cxlmd->cxlds);
663+
664+
if (mds && mds->security.sanitize_active) {
665+
dev_dbg(&cxlmd->dev,
666+
"attempted to commit %s during sanitize\n",
667+
dev_name(&cxld->dev));
668+
return -EBUSY;
669+
}
670+
}
671+
653672
down_read(&cxl_dpa_rwsem);
654673
/* common decoder settings */
655674
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));

drivers/cxl/core/mbox.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,20 +1125,7 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds)
11251125
}
11261126
EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
11271127

1128-
/**
1129-
* cxl_mem_sanitize() - Send a sanitization command to the device.
1130-
* @mds: The device data for the operation
1131-
* @cmd: The specific sanitization command opcode
1132-
*
1133-
* Return: 0 if the command was executed successfully, regardless of
1134-
* whether or not the actual security operation is done in the background,
1135-
* such as for the Sanitize case.
1136-
* Error return values can be the result of the mailbox command, -EINVAL
1137-
* when security requirements are not met or invalid contexts.
1138-
*
1139-
* See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
1140-
*/
1141-
int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
1128+
static int __cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
11421129
{
11431130
int rc;
11441131
u32 sec_out = 0;
@@ -1183,7 +1170,45 @@ int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
11831170

11841171
return 0;
11851172
}
1186-
EXPORT_SYMBOL_NS_GPL(cxl_mem_sanitize, CXL);
1173+
1174+
1175+
/**
1176+
* cxl_mem_sanitize() - Send a sanitization command to the device.
1177+
* @cxlmd: The device for the operation
1178+
* @cmd: The specific sanitization command opcode
1179+
*
1180+
* Return: 0 if the command was executed successfully, regardless of
1181+
* whether or not the actual security operation is done in the background,
1182+
* such as for the Sanitize case.
1183+
* Error return values can be the result of the mailbox command, -EINVAL
1184+
* when security requirements are not met or invalid contexts, or -EBUSY
1185+
* if the sanitize operation is already in flight.
1186+
*
1187+
* See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
1188+
*/
1189+
int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
1190+
{
1191+
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
1192+
struct cxl_port *endpoint;
1193+
int rc;
1194+
1195+
/* synchronize with cxl_mem_probe() and decoder write operations */
1196+
device_lock(&cxlmd->dev);
1197+
endpoint = cxlmd->endpoint;
1198+
down_read(&cxl_region_rwsem);
1199+
/*
1200+
* Require an endpoint to be safe otherwise the driver can not
1201+
* be sure that the device is unmapped.
1202+
*/
1203+
if (endpoint && endpoint->commit_end == -1)
1204+
rc = __cxl_mem_sanitize(mds, cmd);
1205+
else
1206+
rc = -EBUSY;
1207+
up_read(&cxl_region_rwsem);
1208+
device_unlock(&cxlmd->dev);
1209+
1210+
return rc;
1211+
}
11871212

11881213
static int add_dpa_res(struct device *dev, struct resource *parent,
11891214
struct resource *res, resource_size_t start,

drivers/cxl/core/memdev.c

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,16 @@ static ssize_t security_state_show(struct device *dev,
125125
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
126126
struct cxl_dev_state *cxlds = cxlmd->cxlds;
127127
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
128-
u64 reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
129-
u32 pct = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg);
130-
u16 cmd = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK, reg);
131128
unsigned long state = mds->security.state;
129+
int rc = 0;
132130

133-
if (cmd == CXL_MBOX_OP_SANITIZE && pct != 100)
134-
return sysfs_emit(buf, "sanitize\n");
131+
/* sync with latest submission state */
132+
mutex_lock(&mds->mbox_mutex);
133+
if (mds->security.sanitize_active)
134+
rc = sysfs_emit(buf, "sanitize\n");
135+
mutex_unlock(&mds->mbox_mutex);
136+
if (rc)
137+
return rc;
135138

136139
if (!(state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
137140
return sysfs_emit(buf, "disabled\n");
@@ -152,24 +155,17 @@ static ssize_t security_sanitize_store(struct device *dev,
152155
const char *buf, size_t len)
153156
{
154157
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
155-
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
156-
struct cxl_port *port = cxlmd->endpoint;
157158
bool sanitize;
158159
ssize_t rc;
159160

160161
if (kstrtobool(buf, &sanitize) || !sanitize)
161162
return -EINVAL;
162163

163-
if (!port || !is_cxl_endpoint(port))
164-
return -EINVAL;
165-
166-
/* ensure no regions are mapped to this memdev */
167-
if (port->commit_end != -1)
168-
return -EBUSY;
169-
170-
rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SANITIZE);
164+
rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SANITIZE);
165+
if (rc)
166+
return rc;
171167

172-
return rc ? rc : len;
168+
return len;
173169
}
174170
static struct device_attribute dev_attr_security_sanitize =
175171
__ATTR(sanitize, 0200, NULL, security_sanitize_store);
@@ -179,24 +175,17 @@ static ssize_t security_erase_store(struct device *dev,
179175
const char *buf, size_t len)
180176
{
181177
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
182-
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
183-
struct cxl_port *port = cxlmd->endpoint;
184178
ssize_t rc;
185179
bool erase;
186180

187181
if (kstrtobool(buf, &erase) || !erase)
188182
return -EINVAL;
189183

190-
if (!port || !is_cxl_endpoint(port))
191-
return -EINVAL;
192-
193-
/* ensure no regions are mapped to this memdev */
194-
if (port->commit_end != -1)
195-
return -EBUSY;
196-
197-
rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SECURE_ERASE);
184+
rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SECURE_ERASE);
185+
if (rc)
186+
return rc;
198187

199-
return rc ? rc : len;
188+
return len;
200189
}
201190
static struct device_attribute dev_attr_security_erase =
202191
__ATTR(erase, 0200, NULL, security_erase_store);

drivers/cxl/core/port.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
* instantiated by the core.
2929
*/
3030

31+
/*
32+
* All changes to the interleave configuration occur with this lock held
33+
* for write.
34+
*/
35+
DECLARE_RWSEM(cxl_region_rwsem);
36+
3137
static DEFINE_IDA(cxl_port_ida);
3238
static DEFINE_XARRAY(cxl_root_buses);
3339

drivers/cxl/core/region.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@
2828
* 3. Decoder targets
2929
*/
3030

31-
/*
32-
* All changes to the interleave configuration occur with this lock held
33-
* for write.
34-
*/
35-
static DECLARE_RWSEM(cxl_region_rwsem);
36-
3731
static struct cxl_region *to_cxl_region(struct device *dev);
3832

3933
static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,

drivers/cxl/cxlmem.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,15 @@ struct cxl_fw_state {
364364
* @state: state of last security operation
365365
* @enabled_cmds: All security commands enabled in the CEL
366366
* @poll_tmo_secs: polling timeout
367+
* @sanitize_active: sanitize completion pending
367368
* @poll_dwork: polling work item
368369
* @sanitize_node: sanitation sysfs file to notify
369370
*/
370371
struct cxl_security_state {
371372
unsigned long state;
372373
DECLARE_BITMAP(enabled_cmds, CXL_SEC_ENABLED_MAX);
373374
int poll_tmo_secs;
375+
bool sanitize_active;
374376
struct delayed_work poll_dwork;
375377
struct kernfs_node *sanitize_node;
376378
};
@@ -884,7 +886,7 @@ static inline void cxl_mem_active_dec(void)
884886
}
885887
#endif
886888

887-
int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd);
889+
int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
888890

889891
struct cxl_hdm {
890892
struct cxl_component_regs regs;

drivers/cxl/pci.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ static void cxl_mbox_sanitize_work(struct work_struct *work)
154154
mds->security.poll_tmo_secs = 0;
155155
if (mds->security.sanitize_node)
156156
sysfs_notify_dirent(mds->security.sanitize_node);
157+
mds->security.sanitize_active = false;
157158

158159
dev_dbg(cxlds->dev, "Sanitization operation ended\n");
159160
} else {
@@ -292,9 +293,13 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_memdev_state *mds,
292293
* and allow userspace to poll(2) for completion.
293294
*/
294295
if (mbox_cmd->opcode == CXL_MBOX_OP_SANITIZE) {
296+
if (mds->security.sanitize_active)
297+
return -EBUSY;
298+
295299
/* give first timeout a second */
296300
timeout = 1;
297301
mds->security.poll_tmo_secs = timeout;
302+
mds->security.sanitize_active = true;
298303
schedule_delayed_work(&mds->security.poll_dwork,
299304
timeout * HZ);
300305
dev_dbg(dev, "Sanitization operation started\n");

0 commit comments

Comments
 (0)