diff options
| author | Dan Williams <dan.j.williams@intel.com> | 2016-10-06 14:22:37 -0400 |
|---|---|---|
| committer | Dan Williams <dan.j.williams@intel.com> | 2016-10-07 12:20:53 -0400 |
| commit | bd4cd745b3b412ac93227640e3b337962f41d932 (patch) | |
| tree | fc87b054e3d62373f8059e3d5e9bea285489c748 | |
| parent | 8a5f50d3b7f2f601c200f84827c2c9220cd69f71 (diff) | |
tools/testing/nvdimm: support for sub-dividing a pmem region
Update nfit_test to handle multiple sub-allocations within a given pmem
region. The mock resource now tracks and un-tracks sub-ranges as they
are requested and released (either explicitly or via devm callback).
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
| -rw-r--r-- | tools/testing/nvdimm/test/iomap.c | 134 | ||||
| -rw-r--r-- | tools/testing/nvdimm/test/nfit.c | 21 | ||||
| -rw-r--r-- | tools/testing/nvdimm/test/nfit_test.h | 12 |
3 files changed, 124 insertions, 43 deletions
diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index dae5b9b6d186..3ccef732fce9 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c | |||
| @@ -74,7 +74,7 @@ void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, | |||
| 74 | 74 | ||
| 75 | if (nfit_res) | 75 | if (nfit_res) |
| 76 | return (void __iomem *) nfit_res->buf + offset | 76 | return (void __iomem *) nfit_res->buf + offset |
| 77 | - nfit_res->res->start; | 77 | - nfit_res->res.start; |
| 78 | return fallback_fn(offset, size); | 78 | return fallback_fn(offset, size); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| @@ -85,7 +85,7 @@ void __iomem *__wrap_devm_ioremap_nocache(struct device *dev, | |||
| 85 | 85 | ||
| 86 | if (nfit_res) | 86 | if (nfit_res) |
| 87 | return (void __iomem *) nfit_res->buf + offset | 87 | return (void __iomem *) nfit_res->buf + offset |
| 88 | - nfit_res->res->start; | 88 | - nfit_res->res.start; |
| 89 | return devm_ioremap_nocache(dev, offset, size); | 89 | return devm_ioremap_nocache(dev, offset, size); |
| 90 | } | 90 | } |
| 91 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); | 91 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); |
| @@ -96,7 +96,7 @@ void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, | |||
| 96 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 96 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 97 | 97 | ||
| 98 | if (nfit_res) | 98 | if (nfit_res) |
| 99 | return nfit_res->buf + offset - nfit_res->res->start; | 99 | return nfit_res->buf + offset - nfit_res->res.start; |
| 100 | return devm_memremap(dev, offset, size, flags); | 100 | return devm_memremap(dev, offset, size, flags); |
| 101 | } | 101 | } |
| 102 | EXPORT_SYMBOL(__wrap_devm_memremap); | 102 | EXPORT_SYMBOL(__wrap_devm_memremap); |
| @@ -108,7 +108,7 @@ void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res, | |||
| 108 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 108 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 109 | 109 | ||
| 110 | if (nfit_res) | 110 | if (nfit_res) |
| 111 | return nfit_res->buf + offset - nfit_res->res->start; | 111 | return nfit_res->buf + offset - nfit_res->res.start; |
| 112 | return devm_memremap_pages(dev, res, ref, altmap); | 112 | return devm_memremap_pages(dev, res, ref, altmap); |
| 113 | } | 113 | } |
| 114 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); | 114 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); |
| @@ -129,7 +129,7 @@ void *__wrap_memremap(resource_size_t offset, size_t size, | |||
| 129 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 129 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 130 | 130 | ||
| 131 | if (nfit_res) | 131 | if (nfit_res) |
| 132 | return nfit_res->buf + offset - nfit_res->res->start; | 132 | return nfit_res->buf + offset - nfit_res->res.start; |
| 133 | return memremap(offset, size, flags); | 133 | return memremap(offset, size, flags); |
| 134 | } | 134 | } |
| 135 | EXPORT_SYMBOL(__wrap_memremap); | 135 | EXPORT_SYMBOL(__wrap_memremap); |
| @@ -175,6 +175,63 @@ void __wrap_memunmap(void *addr) | |||
| 175 | } | 175 | } |
| 176 | EXPORT_SYMBOL(__wrap_memunmap); | 176 | EXPORT_SYMBOL(__wrap_memunmap); |
| 177 | 177 | ||
| 178 | static bool nfit_test_release_region(struct device *dev, | ||
| 179 | struct resource *parent, resource_size_t start, | ||
| 180 | resource_size_t n); | ||
| 181 | |||
| 182 | static void nfit_devres_release(struct device *dev, void *data) | ||
| 183 | { | ||
| 184 | struct resource *res = *((struct resource **) data); | ||
| 185 | |||
| 186 | WARN_ON(!nfit_test_release_region(NULL, &iomem_resource, res->start, | ||
| 187 | resource_size(res))); | ||
| 188 | } | ||
| 189 | |||
| 190 | static int match(struct device *dev, void *__res, void *match_data) | ||
| 191 | { | ||
| 192 | struct resource *res = *((struct resource **) __res); | ||
| 193 | resource_size_t start = *((resource_size_t *) match_data); | ||
| 194 | |||
| 195 | return res->start == start; | ||
| 196 | } | ||
| 197 | |||
| 198 | static bool nfit_test_release_region(struct device *dev, | ||
| 199 | struct resource *parent, resource_size_t start, | ||
| 200 | resource_size_t n) | ||
| 201 | { | ||
| 202 | if (parent == &iomem_resource) { | ||
| 203 | struct nfit_test_resource *nfit_res = get_nfit_res(start); | ||
| 204 | |||
| 205 | if (nfit_res) { | ||
| 206 | struct nfit_test_request *req; | ||
| 207 | struct resource *res = NULL; | ||
| 208 | |||
| 209 | if (dev) { | ||
| 210 | devres_release(dev, nfit_devres_release, match, | ||
| 211 | &start); | ||
| 212 | return true; | ||
| 213 | } | ||
| 214 | |||
| 215 | spin_lock(&nfit_res->lock); | ||
| 216 | list_for_each_entry(req, &nfit_res->requests, list) | ||
| 217 | if (req->res.start == start) { | ||
| 218 | res = &req->res; | ||
| 219 | list_del(&req->list); | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | spin_unlock(&nfit_res->lock); | ||
| 223 | |||
| 224 | WARN(!res || resource_size(res) != n, | ||
| 225 | "%s: start: %llx n: %llx mismatch: %pr\n", | ||
| 226 | __func__, start, n, res); | ||
| 227 | if (res) | ||
| 228 | kfree(req); | ||
| 229 | return true; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | |||
| 178 | static struct resource *nfit_test_request_region(struct device *dev, | 235 | static struct resource *nfit_test_request_region(struct device *dev, |
| 179 | struct resource *parent, resource_size_t start, | 236 | struct resource *parent, resource_size_t start, |
| 180 | resource_size_t n, const char *name, int flags) | 237 | resource_size_t n, const char *name, int flags) |
| @@ -184,21 +241,57 @@ static struct resource *nfit_test_request_region(struct device *dev, | |||
| 184 | if (parent == &iomem_resource) { | 241 | if (parent == &iomem_resource) { |
| 185 | nfit_res = get_nfit_res(start); | 242 | nfit_res = get_nfit_res(start); |
| 186 | if (nfit_res) { | 243 | if (nfit_res) { |
| 187 | struct resource *res = nfit_res->res + 1; | 244 | struct nfit_test_request *req; |
| 245 | struct resource *res = NULL; | ||
| 188 | 246 | ||
| 189 | if (start + n > nfit_res->res->start | 247 | if (start + n > nfit_res->res.start |
| 190 | + resource_size(nfit_res->res)) { | 248 | + resource_size(&nfit_res->res)) { |
| 191 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", | 249 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", |
| 192 | __func__, start, n, | 250 | __func__, start, n, |
| 193 | nfit_res->res); | 251 | &nfit_res->res); |
| 252 | return NULL; | ||
| 253 | } | ||
| 254 | |||
| 255 | spin_lock(&nfit_res->lock); | ||
| 256 | list_for_each_entry(req, &nfit_res->requests, list) | ||
| 257 | if (start == req->res.start) { | ||
| 258 | res = &req->res; | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | spin_unlock(&nfit_res->lock); | ||
| 262 | |||
| 263 | if (res) { | ||
| 264 | WARN(1, "%pr already busy\n", res); | ||
| 194 | return NULL; | 265 | return NULL; |
| 195 | } | 266 | } |
| 196 | 267 | ||
| 268 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
| 269 | if (!req) | ||
| 270 | return NULL; | ||
| 271 | INIT_LIST_HEAD(&req->list); | ||
| 272 | res = &req->res; | ||
| 273 | |||
| 197 | res->start = start; | 274 | res->start = start; |
| 198 | res->end = start + n - 1; | 275 | res->end = start + n - 1; |
| 199 | res->name = name; | 276 | res->name = name; |
| 200 | res->flags = resource_type(parent); | 277 | res->flags = resource_type(parent); |
| 201 | res->flags |= IORESOURCE_BUSY | flags; | 278 | res->flags |= IORESOURCE_BUSY | flags; |
| 279 | spin_lock(&nfit_res->lock); | ||
| 280 | list_add(&req->list, &nfit_res->requests); | ||
| 281 | spin_unlock(&nfit_res->lock); | ||
| 282 | |||
| 283 | if (dev) { | ||
| 284 | struct resource **d; | ||
| 285 | |||
| 286 | d = devres_alloc(nfit_devres_release, | ||
| 287 | sizeof(struct resource *), | ||
| 288 | GFP_KERNEL); | ||
| 289 | if (!d) | ||
| 290 | return NULL; | ||
| 291 | *d = res; | ||
| 292 | devres_add(dev, d); | ||
| 293 | } | ||
| 294 | |||
| 202 | pr_debug("%s: %pr\n", __func__, res); | 295 | pr_debug("%s: %pr\n", __func__, res); |
| 203 | return res; | 296 | return res; |
| 204 | } | 297 | } |
| @@ -242,29 +335,10 @@ struct resource *__wrap___devm_request_region(struct device *dev, | |||
| 242 | } | 335 | } |
| 243 | EXPORT_SYMBOL(__wrap___devm_request_region); | 336 | EXPORT_SYMBOL(__wrap___devm_request_region); |
| 244 | 337 | ||
| 245 | static bool nfit_test_release_region(struct resource *parent, | ||
| 246 | resource_size_t start, resource_size_t n) | ||
| 247 | { | ||
| 248 | if (parent == &iomem_resource) { | ||
| 249 | struct nfit_test_resource *nfit_res = get_nfit_res(start); | ||
| 250 | if (nfit_res) { | ||
| 251 | struct resource *res = nfit_res->res + 1; | ||
| 252 | |||
| 253 | if (start != res->start || resource_size(res) != n) | ||
| 254 | pr_info("%s: start: %llx n: %llx mismatch: %pr\n", | ||
| 255 | __func__, start, n, res); | ||
| 256 | else | ||
| 257 | memset(res, 0, sizeof(*res)); | ||
| 258 | return true; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | return false; | ||
| 262 | } | ||
| 263 | |||
| 264 | void __wrap___release_region(struct resource *parent, resource_size_t start, | 338 | void __wrap___release_region(struct resource *parent, resource_size_t start, |
| 265 | resource_size_t n) | 339 | resource_size_t n) |
| 266 | { | 340 | { |
| 267 | if (!nfit_test_release_region(parent, start, n)) | 341 | if (!nfit_test_release_region(NULL, parent, start, n)) |
| 268 | __release_region(parent, start, n); | 342 | __release_region(parent, start, n); |
| 269 | } | 343 | } |
| 270 | EXPORT_SYMBOL(__wrap___release_region); | 344 | EXPORT_SYMBOL(__wrap___release_region); |
| @@ -272,7 +346,7 @@ EXPORT_SYMBOL(__wrap___release_region); | |||
| 272 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, | 346 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, |
| 273 | resource_size_t start, resource_size_t n) | 347 | resource_size_t start, resource_size_t n) |
| 274 | { | 348 | { |
| 275 | if (!nfit_test_release_region(parent, start, n)) | 349 | if (!nfit_test_release_region(dev, parent, start, n)) |
| 276 | __devm_release_region(dev, parent, start, n); | 350 | __devm_release_region(dev, parent, start, n); |
| 277 | } | 351 | } |
| 278 | EXPORT_SYMBOL(__wrap___devm_release_region); | 352 | EXPORT_SYMBOL(__wrap___devm_release_region); |
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 175fc24f8f3a..0e721c6fb1cf 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c | |||
| @@ -478,14 +478,12 @@ static struct nfit_test *instances[NUM_NFITS]; | |||
| 478 | static void release_nfit_res(void *data) | 478 | static void release_nfit_res(void *data) |
| 479 | { | 479 | { |
| 480 | struct nfit_test_resource *nfit_res = data; | 480 | struct nfit_test_resource *nfit_res = data; |
| 481 | struct resource *res = nfit_res->res; | ||
| 482 | 481 | ||
| 483 | spin_lock(&nfit_test_lock); | 482 | spin_lock(&nfit_test_lock); |
| 484 | list_del(&nfit_res->list); | 483 | list_del(&nfit_res->list); |
| 485 | spin_unlock(&nfit_test_lock); | 484 | spin_unlock(&nfit_test_lock); |
| 486 | 485 | ||
| 487 | vfree(nfit_res->buf); | 486 | vfree(nfit_res->buf); |
| 488 | kfree(res); | ||
| 489 | kfree(nfit_res); | 487 | kfree(nfit_res); |
| 490 | } | 488 | } |
| 491 | 489 | ||
| @@ -493,12 +491,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
| 493 | void *buf) | 491 | void *buf) |
| 494 | { | 492 | { |
| 495 | struct device *dev = &t->pdev.dev; | 493 | struct device *dev = &t->pdev.dev; |
| 496 | struct resource *res = kzalloc(sizeof(*res) * 2, GFP_KERNEL); | ||
| 497 | struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), | 494 | struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), |
| 498 | GFP_KERNEL); | 495 | GFP_KERNEL); |
| 499 | int rc; | 496 | int rc; |
| 500 | 497 | ||
| 501 | if (!res || !buf || !nfit_res) | 498 | if (!buf || !nfit_res) |
| 502 | goto err; | 499 | goto err; |
| 503 | rc = devm_add_action(dev, release_nfit_res, nfit_res); | 500 | rc = devm_add_action(dev, release_nfit_res, nfit_res); |
| 504 | if (rc) | 501 | if (rc) |
| @@ -507,10 +504,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
| 507 | memset(buf, 0, size); | 504 | memset(buf, 0, size); |
| 508 | nfit_res->dev = dev; | 505 | nfit_res->dev = dev; |
| 509 | nfit_res->buf = buf; | 506 | nfit_res->buf = buf; |
| 510 | nfit_res->res = res; | 507 | nfit_res->res.start = *dma; |
| 511 | res->start = *dma; | 508 | nfit_res->res.end = *dma + size - 1; |
| 512 | res->end = *dma + size - 1; | 509 | nfit_res->res.name = "NFIT"; |
| 513 | res->name = "NFIT"; | 510 | spin_lock_init(&nfit_res->lock); |
| 511 | INIT_LIST_HEAD(&nfit_res->requests); | ||
| 514 | spin_lock(&nfit_test_lock); | 512 | spin_lock(&nfit_test_lock); |
| 515 | list_add(&nfit_res->list, &t->resources); | 513 | list_add(&nfit_res->list, &t->resources); |
| 516 | spin_unlock(&nfit_test_lock); | 514 | spin_unlock(&nfit_test_lock); |
| @@ -519,7 +517,6 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
| 519 | err: | 517 | err: |
| 520 | if (buf) | 518 | if (buf) |
| 521 | vfree(buf); | 519 | vfree(buf); |
| 522 | kfree(res); | ||
| 523 | kfree(nfit_res); | 520 | kfree(nfit_res); |
| 524 | return NULL; | 521 | return NULL; |
| 525 | } | 522 | } |
| @@ -544,13 +541,13 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) | |||
| 544 | continue; | 541 | continue; |
| 545 | spin_lock(&nfit_test_lock); | 542 | spin_lock(&nfit_test_lock); |
| 546 | list_for_each_entry(n, &t->resources, list) { | 543 | list_for_each_entry(n, &t->resources, list) { |
| 547 | if (addr >= n->res->start && (addr < n->res->start | 544 | if (addr >= n->res.start && (addr < n->res.start |
| 548 | + resource_size(n->res))) { | 545 | + resource_size(&n->res))) { |
| 549 | nfit_res = n; | 546 | nfit_res = n; |
| 550 | break; | 547 | break; |
| 551 | } else if (addr >= (unsigned long) n->buf | 548 | } else if (addr >= (unsigned long) n->buf |
| 552 | && (addr < (unsigned long) n->buf | 549 | && (addr < (unsigned long) n->buf |
| 553 | + resource_size(n->res))) { | 550 | + resource_size(&n->res))) { |
| 554 | nfit_res = n; | 551 | nfit_res = n; |
| 555 | break; | 552 | break; |
| 556 | } | 553 | } |
diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index 9f18e2a4a862..c281dd2e5e2d 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h | |||
| @@ -13,11 +13,21 @@ | |||
| 13 | #ifndef __NFIT_TEST_H__ | 13 | #ifndef __NFIT_TEST_H__ |
| 14 | #define __NFIT_TEST_H__ | 14 | #define __NFIT_TEST_H__ |
| 15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
| 16 | #include <linux/ioport.h> | ||
| 17 | #include <linux/spinlock_types.h> | ||
| 18 | |||
| 19 | struct nfit_test_request { | ||
| 20 | struct list_head list; | ||
| 21 | struct resource res; | ||
| 22 | }; | ||
| 16 | 23 | ||
| 17 | struct nfit_test_resource { | 24 | struct nfit_test_resource { |
| 25 | struct list_head requests; | ||
| 18 | struct list_head list; | 26 | struct list_head list; |
| 19 | struct resource *res; | 27 | struct resource res; |
| 20 | struct device *dev; | 28 | struct device *dev; |
| 29 | spinlock_t lock; | ||
| 30 | int req_count; | ||
| 21 | void *buf; | 31 | void *buf; |
| 22 | }; | 32 | }; |
| 23 | 33 | ||
