Skip to content

Commit ba9c8dd

Browse files
committed
acpi, nfit: add dimm device notification support
Per "ACPI 6.1 Section 9.20.3" NVDIMM devices, children of the ACPI0012 NVDIMM Root device, can receive health event notifications. Given that these devices are precluded from registering a notification handler via acpi_driver.acpi_device_ops (due to no _HID), we use acpi_install_notify_handler() directly. The registered handler, acpi_nvdimm_notify(), triggers a poll(2) event on the nmemX/nfit/flags sysfs attribute when a health event notification is received. Cc: Rafael J. Wysocki <[email protected]> Tested-by: Toshi Kani <[email protected]> Reviewed-by: Vishal Verma <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]> Reviewed-by: Toshi Kani <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent c14a868 commit ba9c8dd

File tree

4 files changed

+103
-4
lines changed

4 files changed

+103
-4
lines changed

drivers/acpi/nfit/core.c

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,43 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
12481248
return NULL;
12491249
}
12501250

1251+
static void __acpi_nvdimm_notify(struct device *dev, u32 event)
1252+
{
1253+
struct nfit_mem *nfit_mem;
1254+
struct acpi_nfit_desc *acpi_desc;
1255+
1256+
dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__,
1257+
event);
1258+
1259+
if (event != NFIT_NOTIFY_DIMM_HEALTH) {
1260+
dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev),
1261+
event);
1262+
return;
1263+
}
1264+
1265+
acpi_desc = dev_get_drvdata(dev->parent);
1266+
if (!acpi_desc)
1267+
return;
1268+
1269+
/*
1270+
* If we successfully retrieved acpi_desc, then we know nfit_mem data
1271+
* is still valid.
1272+
*/
1273+
nfit_mem = dev_get_drvdata(dev);
1274+
if (nfit_mem && nfit_mem->flags_attr)
1275+
sysfs_notify_dirent(nfit_mem->flags_attr);
1276+
}
1277+
1278+
static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
1279+
{
1280+
struct acpi_device *adev = data;
1281+
struct device *dev = &adev->dev;
1282+
1283+
device_lock(dev->parent);
1284+
__acpi_nvdimm_notify(dev, event);
1285+
device_unlock(dev->parent);
1286+
}
1287+
12511288
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
12521289
struct nfit_mem *nfit_mem, u32 device_handle)
12531290
{
@@ -1272,6 +1309,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
12721309
return force_enable_dimms ? 0 : -ENODEV;
12731310
}
12741311

1312+
if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle,
1313+
ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) {
1314+
dev_err(dev, "%s: notification registration failed\n",
1315+
dev_name(&adev_dimm->dev));
1316+
return -ENXIO;
1317+
}
1318+
12751319
/*
12761320
* Until standardization materializes we need to consider 4
12771321
* different command sets. Note, that checking for function0 (bit0)
@@ -1310,18 +1354,38 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
13101354
return 0;
13111355
}
13121356

1357+
static void shutdown_dimm_notify(void *data)
1358+
{
1359+
struct acpi_nfit_desc *acpi_desc = data;
1360+
struct nfit_mem *nfit_mem;
1361+
1362+
mutex_lock(&acpi_desc->init_mutex);
1363+
/*
1364+
* Clear out the nfit_mem->flags_attr and shut down dimm event
1365+
* notifications.
1366+
*/
1367+
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1368+
if (nfit_mem->flags_attr) {
1369+
sysfs_put(nfit_mem->flags_attr);
1370+
nfit_mem->flags_attr = NULL;
1371+
}
1372+
acpi_remove_notify_handler(nfit_mem->adev->handle,
1373+
ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify);
1374+
}
1375+
mutex_unlock(&acpi_desc->init_mutex);
1376+
}
1377+
13131378
static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
13141379
{
13151380
struct nfit_mem *nfit_mem;
1316-
int dimm_count = 0;
1381+
int dimm_count = 0, rc;
1382+
struct nvdimm *nvdimm;
13171383

13181384
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
13191385
struct acpi_nfit_flush_address *flush;
13201386
unsigned long flags = 0, cmd_mask;
1321-
struct nvdimm *nvdimm;
13221387
u32 device_handle;
13231388
u16 mem_flags;
1324-
int rc;
13251389

13261390
device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
13271391
nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
@@ -1374,7 +1438,30 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
13741438

13751439
}
13761440

1377-
return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
1441+
rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
1442+
if (rc)
1443+
return rc;
1444+
1445+
/*
1446+
* Now that dimms are successfully registered, and async registration
1447+
* is flushed, attempt to enable event notification.
1448+
*/
1449+
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1450+
struct kernfs_node *nfit_kernfs;
1451+
1452+
nvdimm = nfit_mem->nvdimm;
1453+
nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
1454+
if (nfit_kernfs)
1455+
nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
1456+
"flags");
1457+
sysfs_put(nfit_kernfs);
1458+
if (!nfit_mem->flags_attr)
1459+
dev_warn(acpi_desc->dev, "%s: notifications disabled\n",
1460+
nvdimm_name(nvdimm));
1461+
}
1462+
1463+
return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify,
1464+
acpi_desc);
13781465
}
13791466

13801467
static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)

drivers/acpi/nfit/nfit.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ enum nfit_root_notifiers {
8282
NFIT_NOTIFY_UPDATE = 0x80,
8383
};
8484

85+
enum nfit_dimm_notifiers {
86+
NFIT_NOTIFY_DIMM_HEALTH = 0x81,
87+
};
88+
8589
struct nfit_spa {
8690
struct list_head list;
8791
struct nd_region *nd_region;
@@ -128,6 +132,7 @@ struct nfit_mem {
128132
struct acpi_nfit_system_address *spa_bdw;
129133
struct acpi_nfit_interleave *idt_dcr;
130134
struct acpi_nfit_interleave *idt_bdw;
135+
struct kernfs_node *flags_attr;
131136
struct nfit_flush *nfit_flush;
132137
struct list_head list;
133138
struct acpi_device *adev;

drivers/nvdimm/dimm_devs.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm)
263263
}
264264
EXPORT_SYMBOL_GPL(nvdimm_name);
265265

266+
struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
267+
{
268+
return &nvdimm->dev.kobj;
269+
}
270+
EXPORT_SYMBOL_GPL(nvdimm_kobj);
271+
266272
unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
267273
{
268274
return nvdimm->cmd_mask;

include/linux/libnvdimm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct nd_blk_region *to_nd_blk_region(struct device *dev);
139139
struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
140140
struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus);
141141
const char *nvdimm_name(struct nvdimm *nvdimm);
142+
struct kobject *nvdimm_kobj(struct nvdimm *nvdimm);
142143
unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
143144
void *nvdimm_provider_data(struct nvdimm *nvdimm);
144145
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,

0 commit comments

Comments
 (0)