Skip to content

Commit 7ae0fa4

Browse files
committed
nfit, libnvdimm: async region scrub workqueue
Introduce a workqueue that will be used to run address range scrub asynchronously with the rest of nvdimm device probing. Userspace still wants notification when probing operations complete, so introduce a new callback to flush this workqueue when userspace is awaiting probe completion. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent a61fe6f commit 7ae0fa4

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

drivers/acpi/nfit.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ static bool force_enable_dimms;
3434
module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR);
3535
MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status");
3636

37+
static struct workqueue_struct *nfit_wq;
38+
3739
struct nfit_table_prev {
3840
struct list_head spas;
3941
struct list_head memdevs;
@@ -1961,6 +1963,39 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
19611963
}
19621964
EXPORT_SYMBOL_GPL(acpi_nfit_init);
19631965

1966+
struct acpi_nfit_flush_work {
1967+
struct work_struct work;
1968+
struct completion cmp;
1969+
};
1970+
1971+
static void flush_probe(struct work_struct *work)
1972+
{
1973+
struct acpi_nfit_flush_work *flush;
1974+
1975+
flush = container_of(work, typeof(*flush), work);
1976+
complete(&flush->cmp);
1977+
}
1978+
1979+
static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
1980+
{
1981+
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
1982+
struct device *dev = acpi_desc->dev;
1983+
struct acpi_nfit_flush_work flush;
1984+
1985+
/* bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
1986+
device_lock(dev);
1987+
device_unlock(dev);
1988+
1989+
/*
1990+
* Scrub work could take 10s of seconds, userspace may give up so we
1991+
* need to be interruptible while waiting.
1992+
*/
1993+
INIT_WORK_ONSTACK(&flush.work, flush_probe);
1994+
COMPLETION_INITIALIZER_ONSTACK(flush.cmp);
1995+
queue_work(nfit_wq, &flush.work);
1996+
return wait_for_completion_interruptible(&flush.cmp);
1997+
}
1998+
19641999
void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
19652000
{
19662001
struct nvdimm_bus_descriptor *nd_desc;
@@ -1971,6 +2006,7 @@ void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
19712006
nd_desc = &acpi_desc->nd_desc;
19722007
nd_desc->provider_name = "ACPI.NFIT";
19732008
nd_desc->ndctl = acpi_nfit_ctl;
2009+
nd_desc->flush_probe = acpi_nfit_flush_probe;
19742010
nd_desc->attr_groups = acpi_nfit_attribute_groups;
19752011

19762012
INIT_LIST_HEAD(&acpi_desc->spa_maps);
@@ -2048,6 +2084,8 @@ static int acpi_nfit_remove(struct acpi_device *adev)
20482084
{
20492085
struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev);
20502086

2087+
acpi_desc->cancel = 1;
2088+
flush_workqueue(nfit_wq);
20512089
nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
20522090
return 0;
20532091
}
@@ -2079,6 +2117,12 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
20792117
acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, &acpi_desc->nd_desc);
20802118
if (!acpi_desc->nvdimm_bus)
20812119
goto out_unlock;
2120+
} else {
2121+
/*
2122+
* Finish previous registration before considering new
2123+
* regions.
2124+
*/
2125+
flush_workqueue(nfit_wq);
20822126
}
20832127

20842128
/* Evaluate _FIT */
@@ -2146,12 +2190,17 @@ static __init int nfit_init(void)
21462190
acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]);
21472191
acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]);
21482192

2193+
nfit_wq = create_singlethread_workqueue("nfit");
2194+
if (!nfit_wq)
2195+
return -ENOMEM;
2196+
21492197
return acpi_bus_register_driver(&acpi_nfit_driver);
21502198
}
21512199

21522200
static __exit void nfit_exit(void)
21532201
{
21542202
acpi_bus_unregister_driver(&acpi_nfit_driver);
2203+
destroy_workqueue(nfit_wq);
21552204
}
21562205

21572206
module_init(nfit_init);

drivers/acpi/nfit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
#ifndef __NFIT_H__
1616
#define __NFIT_H__
17+
#include <linux/workqueue.h>
1718
#include <linux/libnvdimm.h>
1819
#include <linux/types.h>
1920
#include <linux/uuid.h>
@@ -123,6 +124,8 @@ struct acpi_nfit_desc {
123124
struct list_head idts;
124125
struct nvdimm_bus *nvdimm_bus;
125126
struct device *dev;
127+
struct work_struct work;
128+
unsigned int cancel:1;
126129
unsigned long dimm_dsm_force_en;
127130
unsigned long bus_dsm_force_en;
128131
int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,

drivers/nvdimm/core.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,15 @@ static int flush_regions_dimms(struct device *dev, void *data)
298298
static ssize_t wait_probe_show(struct device *dev,
299299
struct device_attribute *attr, char *buf)
300300
{
301+
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
302+
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
303+
int rc;
304+
305+
if (nd_desc->flush_probe) {
306+
rc = nd_desc->flush_probe(nd_desc);
307+
if (rc)
308+
return rc;
309+
}
301310
nd_synchronize();
302311
device_for_each_child(dev, NULL, flush_regions_dimms);
303312
return sprintf(buf, "1\n");

include/linux/libnvdimm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct nvdimm_bus_descriptor {
7171
unsigned long dsm_mask;
7272
char *provider_name;
7373
ndctl_fn ndctl;
74+
int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
7475
};
7576

7677
struct nd_cmd_desc {

0 commit comments

Comments
 (0)