Skip to content

Commit 23f4984

Browse files
committed
libnvdimm: rework region badblocks clearing
Toshi noticed that the new support for a region-level badblocks missed the case where errors are cleared due to BTT I/O. An initial attempt to fix this ran into a "sleeping while atomic" warning due to taking the nvdimm_bus_lock() in the BTT I/O path to satisfy the locking requirements of __nvdimm_bus_badblocks_clear(). However, that lock is not needed since we are not acting on any data that is subject to change under that lock. The badblocks instance has its own internal lock to handle mutations of the error list. So, in order to make it clear that we are just acting on region devices, rename __nvdimm_bus_badblocks_clear() to nvdimm_clear_badblocks_regions(). Eliminate the lock and consolidate all support routines for the new nvdimm_account_cleared_poison() in drivers/nvdimm/bus.c. Finally, to the opportunity to cleanup to some unnecessary casts, make the calling convention of nvdimm_clear_badblocks_regions() clearer by replacing struct resource with the minimal struct clear_badblocks_context, and use the DEVICE_ATTR macro. Cc: Dave Jiang <dave.jiang@intel.com> Cc: Vishal Verma <vishal.l.verma@intel.com> Reported-by: Toshi Kani <toshi.kani@hpe.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 7699a6a commit 23f4984

File tree

4 files changed

+59
-60
lines changed

4 files changed

+59
-60
lines changed

drivers/nvdimm/bus.c

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,57 @@ void nvdimm_region_notify(struct nd_region *nd_region, enum nvdimm_event event)
172172
}
173173
EXPORT_SYMBOL_GPL(nvdimm_region_notify);
174174

175+
struct clear_badblocks_context {
176+
resource_size_t phys, cleared;
177+
};
178+
179+
static int nvdimm_clear_badblocks_region(struct device *dev, void *data)
180+
{
181+
struct clear_badblocks_context *ctx = data;
182+
struct nd_region *nd_region;
183+
resource_size_t ndr_end;
184+
sector_t sector;
185+
186+
/* make sure device is a region */
187+
if (!is_nd_pmem(dev))
188+
return 0;
189+
190+
nd_region = to_nd_region(dev);
191+
ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1;
192+
193+
/* make sure we are in the region */
194+
if (ctx->phys < nd_region->ndr_start
195+
|| (ctx->phys + ctx->cleared) > ndr_end)
196+
return 0;
197+
198+
sector = (ctx->phys - nd_region->ndr_start) / 512;
199+
badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512);
200+
201+
return 0;
202+
}
203+
204+
static void nvdimm_clear_badblocks_regions(struct nvdimm_bus *nvdimm_bus,
205+
phys_addr_t phys, u64 cleared)
206+
{
207+
struct clear_badblocks_context ctx = {
208+
.phys = phys,
209+
.cleared = cleared,
210+
};
211+
212+
device_for_each_child(&nvdimm_bus->dev, &ctx,
213+
nvdimm_clear_badblocks_region);
214+
}
215+
216+
static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus,
217+
phys_addr_t phys, u64 cleared)
218+
{
219+
if (cleared > 0)
220+
nvdimm_forget_poison(nvdimm_bus, phys, cleared);
221+
222+
if (cleared > 0 && cleared / 512)
223+
nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared);
224+
}
225+
175226
long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
176227
unsigned int len)
177228
{
@@ -219,22 +270,12 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
219270
if (cmd_rc < 0)
220271
return cmd_rc;
221272

222-
if (clear_err.cleared > 0)
223-
nvdimm_forget_poison(nvdimm_bus, phys, clear_err.cleared);
273+
nvdimm_account_cleared_poison(nvdimm_bus, phys, clear_err.cleared);
224274

225275
return clear_err.cleared;
226276
}
227277
EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
228278

229-
void __nvdimm_bus_badblocks_clear(struct nvdimm_bus *nvdimm_bus,
230-
struct resource *res)
231-
{
232-
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
233-
device_for_each_child(&nvdimm_bus->dev, (void *)res,
234-
nvdimm_region_badblocks_clear);
235-
}
236-
EXPORT_SYMBOL_GPL(__nvdimm_bus_badblocks_clear);
237-
238279
static int nvdimm_bus_match(struct device *dev, struct device_driver *drv);
239280

240281
static struct bus_type nvdimm_bus_type = {
@@ -989,18 +1030,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
9891030

9901031
if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) {
9911032
struct nd_cmd_clear_error *clear_err = buf;
992-
struct resource res;
993-
994-
if (clear_err->cleared) {
995-
/* clearing the poison list we keep track of */
996-
nvdimm_forget_poison(nvdimm_bus, clear_err->address,
997-
clear_err->cleared);
9981033

999-
/* now sync the badblocks lists */
1000-
res.start = clear_err->address;
1001-
res.end = clear_err->address + clear_err->cleared - 1;
1002-
__nvdimm_bus_badblocks_clear(nvdimm_bus, &res);
1003-
}
1034+
nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address,
1035+
clear_err->cleared);
10041036
}
10051037
nvdimm_bus_unlock(&nvdimm_bus->dev);
10061038

drivers/nvdimm/region.c

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -131,31 +131,6 @@ static void nd_region_notify(struct device *dev, enum nvdimm_event event)
131131
device_for_each_child(dev, &event, child_notify);
132132
}
133133

134-
int nvdimm_region_badblocks_clear(struct device *dev, void *data)
135-
{
136-
struct resource *res = (struct resource *)data;
137-
struct nd_region *nd_region;
138-
resource_size_t ndr_end;
139-
sector_t sector;
140-
141-
/* make sure device is a region */
142-
if (!is_nd_pmem(dev))
143-
return 0;
144-
145-
nd_region = to_nd_region(dev);
146-
ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1;
147-
148-
/* make sure we are in the region */
149-
if (res->start < nd_region->ndr_start || res->end > ndr_end)
150-
return 0;
151-
152-
sector = (res->start - nd_region->ndr_start) >> 9;
153-
badblocks_clear(&nd_region->bb, sector, resource_size(res) >> 9);
154-
155-
return 0;
156-
}
157-
EXPORT_SYMBOL_GPL(nvdimm_region_badblocks_clear);
158-
159134
static struct nd_device_driver nd_region_driver = {
160135
.probe = nd_region_probe,
161136
.remove = nd_region_remove,

drivers/nvdimm/region_devs.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -477,20 +477,15 @@ static ssize_t read_only_store(struct device *dev,
477477
}
478478
static DEVICE_ATTR_RW(read_only);
479479

480-
static ssize_t nd_badblocks_show(struct device *dev,
480+
static ssize_t region_badblocks_show(struct device *dev,
481481
struct device_attribute *attr, char *buf)
482482
{
483483
struct nd_region *nd_region = to_nd_region(dev);
484484

485485
return badblocks_show(&nd_region->bb, buf, 0);
486486
}
487-
static struct device_attribute dev_attr_nd_badblocks = {
488-
.attr = {
489-
.name = "badblocks",
490-
.mode = S_IRUGO
491-
},
492-
.show = nd_badblocks_show,
493-
};
487+
488+
static DEVICE_ATTR(badblocks, 0444, region_badblocks_show, NULL);
494489

495490
static ssize_t resource_show(struct device *dev,
496491
struct device_attribute *attr, char *buf)
@@ -514,7 +509,7 @@ static struct attribute *nd_region_attributes[] = {
514509
&dev_attr_available_size.attr,
515510
&dev_attr_namespace_seed.attr,
516511
&dev_attr_init_namespaces.attr,
517-
&dev_attr_nd_badblocks.attr,
512+
&dev_attr_badblocks.attr,
518513
&dev_attr_resource.attr,
519514
NULL,
520515
};
@@ -532,7 +527,7 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n)
532527
if (!is_nd_pmem(dev) && a == &dev_attr_dax_seed.attr)
533528
return 0;
534529

535-
if (!is_nd_pmem(dev) && a == &dev_attr_nd_badblocks.attr)
530+
if (!is_nd_pmem(dev) && a == &dev_attr_badblocks.attr)
536531
return 0;
537532

538533
if (!is_nd_pmem(dev) && a == &dev_attr_resource.attr)

include/linux/libnvdimm.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,4 @@ void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane);
162162
u64 nd_fletcher64(void *addr, size_t len, bool le);
163163
void nvdimm_flush(struct nd_region *nd_region);
164164
int nvdimm_has_flush(struct nd_region *nd_region);
165-
int nvdimm_region_badblocks_clear(struct device *dev, void *data);
166-
void __nvdimm_bus_badblocks_clear(struct nvdimm_bus *nvdimm_bus,
167-
struct resource *res);
168165
#endif /* __LIBNVDIMM_H__ */

0 commit comments

Comments
 (0)