diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-18 13:52:08 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-18 13:52:08 -0400 |
commit | f8c3500cd137867927bc080f4a6e02e0222dd1b8 (patch) | |
tree | 4bbcdedca12ec9d4db6f48f37052c983abc387b4 | |
parent | d77e9e4e18ce9da3b4981a5c537979c42b06638c (diff) | |
parent | 8c2e408e73f735d2e6e8b43f9b038c9abb082939 (diff) |
Merge tag 'libnvdimm-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
"Primarily just the virtio_pmem driver:
- virtio_pmem
The new virtio_pmem facility introduces a paravirtualized
persistent memory device that allows a guest VM to use DAX
mechanisms to access a host-file with host-page-cache. It arranges
for MAP_SYNC to be disabled and instead triggers a host fsync()
when a 'write-cache flush' command is sent to the virtual disk
device.
- Miscellaneous small fixups"
* tag 'libnvdimm-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
virtio_pmem: fix sparse warning
xfs: disable map_sync for async flush
ext4: disable map_sync for async flush
dax: check synchronous mapping is supported
dm: enable synchronous dax
libnvdimm: add dax_dev sync flag
virtio-pmem: Add virtio pmem driver
libnvdimm: nd_region flush callback support
libnvdimm, namespace: Drop uuid_t implementation detail
-rw-r--r-- | drivers/acpi/nfit/core.c | 4 | ||||
-rw-r--r-- | drivers/dax/bus.c | 2 | ||||
-rw-r--r-- | drivers/dax/super.c | 19 | ||||
-rw-r--r-- | drivers/md/dm-table.c | 24 | ||||
-rw-r--r-- | drivers/md/dm.c | 5 | ||||
-rw-r--r-- | drivers/md/dm.h | 5 | ||||
-rw-r--r-- | drivers/nvdimm/Makefile | 1 | ||||
-rw-r--r-- | drivers/nvdimm/claim.c | 6 | ||||
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 8 | ||||
-rw-r--r-- | drivers/nvdimm/nd.h | 1 | ||||
-rw-r--r-- | drivers/nvdimm/nd_virtio.c | 125 | ||||
-rw-r--r-- | drivers/nvdimm/pmem.c | 18 | ||||
-rw-r--r-- | drivers/nvdimm/region_devs.c | 33 | ||||
-rw-r--r-- | drivers/nvdimm/virtio_pmem.c | 122 | ||||
-rw-r--r-- | drivers/nvdimm/virtio_pmem.h | 55 | ||||
-rw-r--r-- | drivers/s390/block/dcssblk.c | 2 | ||||
-rw-r--r-- | drivers/virtio/Kconfig | 11 | ||||
-rw-r--r-- | fs/ext4/file.c | 10 | ||||
-rw-r--r-- | fs/xfs/xfs_file.c | 9 | ||||
-rw-r--r-- | include/linux/dax.h | 41 | ||||
-rw-r--r-- | include/linux/libnvdimm.h | 10 | ||||
-rw-r--r-- | include/uapi/linux/virtio_ids.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/virtio_pmem.h | 34 |
23 files changed, 508 insertions, 38 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 23022cf20d26..c02fa27dd3f3 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c | |||
@@ -2426,7 +2426,7 @@ static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, | |||
2426 | offset = to_interleave_offset(offset, mmio); | 2426 | offset = to_interleave_offset(offset, mmio); |
2427 | 2427 | ||
2428 | writeq(cmd, mmio->addr.base + offset); | 2428 | writeq(cmd, mmio->addr.base + offset); |
2429 | nvdimm_flush(nfit_blk->nd_region); | 2429 | nvdimm_flush(nfit_blk->nd_region, NULL); |
2430 | 2430 | ||
2431 | if (nfit_blk->dimm_flags & NFIT_BLK_DCR_LATCH) | 2431 | if (nfit_blk->dimm_flags & NFIT_BLK_DCR_LATCH) |
2432 | readq(mmio->addr.base + offset); | 2432 | readq(mmio->addr.base + offset); |
@@ -2475,7 +2475,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, | |||
2475 | } | 2475 | } |
2476 | 2476 | ||
2477 | if (rw) | 2477 | if (rw) |
2478 | nvdimm_flush(nfit_blk->nd_region); | 2478 | nvdimm_flush(nfit_blk->nd_region, NULL); |
2479 | 2479 | ||
2480 | rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; | 2480 | rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; |
2481 | return rc; | 2481 | return rc; |
diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 2109cfe80219..5f184e751c82 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c | |||
@@ -388,7 +388,7 @@ struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, | |||
388 | * No 'host' or dax_operations since there is no access to this | 388 | * No 'host' or dax_operations since there is no access to this |
389 | * device outside of mmap of the resulting character device. | 389 | * device outside of mmap of the resulting character device. |
390 | */ | 390 | */ |
391 | dax_dev = alloc_dax(dev_dax, NULL, NULL); | 391 | dax_dev = alloc_dax(dev_dax, NULL, NULL, DAXDEV_F_SYNC); |
392 | if (!dax_dev) | 392 | if (!dax_dev) |
393 | goto err; | 393 | goto err; |
394 | 394 | ||
diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 4e5ae7e8b557..8ab12068eea3 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c | |||
@@ -195,6 +195,8 @@ enum dax_device_flags { | |||
195 | DAXDEV_ALIVE, | 195 | DAXDEV_ALIVE, |
196 | /* gate whether dax_flush() calls the low level flush routine */ | 196 | /* gate whether dax_flush() calls the low level flush routine */ |
197 | DAXDEV_WRITE_CACHE, | 197 | DAXDEV_WRITE_CACHE, |
198 | /* flag to check if device supports synchronous flush */ | ||
199 | DAXDEV_SYNC, | ||
198 | }; | 200 | }; |
199 | 201 | ||
200 | /** | 202 | /** |
@@ -372,6 +374,18 @@ bool dax_write_cache_enabled(struct dax_device *dax_dev) | |||
372 | } | 374 | } |
373 | EXPORT_SYMBOL_GPL(dax_write_cache_enabled); | 375 | EXPORT_SYMBOL_GPL(dax_write_cache_enabled); |
374 | 376 | ||
377 | bool __dax_synchronous(struct dax_device *dax_dev) | ||
378 | { | ||
379 | return test_bit(DAXDEV_SYNC, &dax_dev->flags); | ||
380 | } | ||
381 | EXPORT_SYMBOL_GPL(__dax_synchronous); | ||
382 | |||
383 | void __set_dax_synchronous(struct dax_device *dax_dev) | ||
384 | { | ||
385 | set_bit(DAXDEV_SYNC, &dax_dev->flags); | ||
386 | } | ||
387 | EXPORT_SYMBOL_GPL(__set_dax_synchronous); | ||
388 | |||
375 | bool dax_alive(struct dax_device *dax_dev) | 389 | bool dax_alive(struct dax_device *dax_dev) |
376 | { | 390 | { |
377 | lockdep_assert_held(&dax_srcu); | 391 | lockdep_assert_held(&dax_srcu); |
@@ -526,7 +540,7 @@ static void dax_add_host(struct dax_device *dax_dev, const char *host) | |||
526 | } | 540 | } |
527 | 541 | ||
528 | struct dax_device *alloc_dax(void *private, const char *__host, | 542 | struct dax_device *alloc_dax(void *private, const char *__host, |
529 | const struct dax_operations *ops) | 543 | const struct dax_operations *ops, unsigned long flags) |
530 | { | 544 | { |
531 | struct dax_device *dax_dev; | 545 | struct dax_device *dax_dev; |
532 | const char *host; | 546 | const char *host; |
@@ -549,6 +563,9 @@ struct dax_device *alloc_dax(void *private, const char *__host, | |||
549 | dax_add_host(dax_dev, host); | 563 | dax_add_host(dax_dev, host); |
550 | dax_dev->ops = ops; | 564 | dax_dev->ops = ops; |
551 | dax_dev->private = private; | 565 | dax_dev->private = private; |
566 | if (flags & DAXDEV_F_SYNC) | ||
567 | set_dax_synchronous(dax_dev); | ||
568 | |||
552 | return dax_dev; | 569 | return dax_dev; |
553 | 570 | ||
554 | err_dev: | 571 | err_dev: |
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index ec8b27e20de3..caaee8032afe 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c | |||
@@ -881,7 +881,7 @@ void dm_table_set_type(struct dm_table *t, enum dm_queue_mode type) | |||
881 | EXPORT_SYMBOL_GPL(dm_table_set_type); | 881 | EXPORT_SYMBOL_GPL(dm_table_set_type); |
882 | 882 | ||
883 | /* validate the dax capability of the target device span */ | 883 | /* validate the dax capability of the target device span */ |
884 | static int device_supports_dax(struct dm_target *ti, struct dm_dev *dev, | 884 | int device_supports_dax(struct dm_target *ti, struct dm_dev *dev, |
885 | sector_t start, sector_t len, void *data) | 885 | sector_t start, sector_t len, void *data) |
886 | { | 886 | { |
887 | int blocksize = *(int *) data; | 887 | int blocksize = *(int *) data; |
@@ -890,7 +890,15 @@ static int device_supports_dax(struct dm_target *ti, struct dm_dev *dev, | |||
890 | start, len); | 890 | start, len); |
891 | } | 891 | } |
892 | 892 | ||
893 | bool dm_table_supports_dax(struct dm_table *t, int blocksize) | 893 | /* Check devices support synchronous DAX */ |
894 | static int device_synchronous(struct dm_target *ti, struct dm_dev *dev, | ||
895 | sector_t start, sector_t len, void *data) | ||
896 | { | ||
897 | return dax_synchronous(dev->dax_dev); | ||
898 | } | ||
899 | |||
900 | bool dm_table_supports_dax(struct dm_table *t, | ||
901 | iterate_devices_callout_fn iterate_fn, int *blocksize) | ||
894 | { | 902 | { |
895 | struct dm_target *ti; | 903 | struct dm_target *ti; |
896 | unsigned i; | 904 | unsigned i; |
@@ -903,8 +911,7 @@ bool dm_table_supports_dax(struct dm_table *t, int blocksize) | |||
903 | return false; | 911 | return false; |
904 | 912 | ||
905 | if (!ti->type->iterate_devices || | 913 | if (!ti->type->iterate_devices || |
906 | !ti->type->iterate_devices(ti, device_supports_dax, | 914 | !ti->type->iterate_devices(ti, iterate_fn, blocksize)) |
907 | &blocksize)) | ||
908 | return false; | 915 | return false; |
909 | } | 916 | } |
910 | 917 | ||
@@ -940,6 +947,7 @@ static int dm_table_determine_type(struct dm_table *t) | |||
940 | struct dm_target *tgt; | 947 | struct dm_target *tgt; |
941 | struct list_head *devices = dm_table_get_devices(t); | 948 | struct list_head *devices = dm_table_get_devices(t); |
942 | enum dm_queue_mode live_md_type = dm_get_md_type(t->md); | 949 | enum dm_queue_mode live_md_type = dm_get_md_type(t->md); |
950 | int page_size = PAGE_SIZE; | ||
943 | 951 | ||
944 | if (t->type != DM_TYPE_NONE) { | 952 | if (t->type != DM_TYPE_NONE) { |
945 | /* target already set the table's type */ | 953 | /* target already set the table's type */ |
@@ -984,7 +992,7 @@ static int dm_table_determine_type(struct dm_table *t) | |||
984 | verify_bio_based: | 992 | verify_bio_based: |
985 | /* We must use this table as bio-based */ | 993 | /* We must use this table as bio-based */ |
986 | t->type = DM_TYPE_BIO_BASED; | 994 | t->type = DM_TYPE_BIO_BASED; |
987 | if (dm_table_supports_dax(t, PAGE_SIZE) || | 995 | if (dm_table_supports_dax(t, device_supports_dax, &page_size) || |
988 | (list_empty(devices) && live_md_type == DM_TYPE_DAX_BIO_BASED)) { | 996 | (list_empty(devices) && live_md_type == DM_TYPE_DAX_BIO_BASED)) { |
989 | t->type = DM_TYPE_DAX_BIO_BASED; | 997 | t->type = DM_TYPE_DAX_BIO_BASED; |
990 | } else { | 998 | } else { |
@@ -1883,6 +1891,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, | |||
1883 | struct queue_limits *limits) | 1891 | struct queue_limits *limits) |
1884 | { | 1892 | { |
1885 | bool wc = false, fua = false; | 1893 | bool wc = false, fua = false; |
1894 | int page_size = PAGE_SIZE; | ||
1886 | 1895 | ||
1887 | /* | 1896 | /* |
1888 | * Copy table's limits to the DM device's request_queue | 1897 | * Copy table's limits to the DM device's request_queue |
@@ -1910,8 +1919,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, | |||
1910 | } | 1919 | } |
1911 | blk_queue_write_cache(q, wc, fua); | 1920 | blk_queue_write_cache(q, wc, fua); |
1912 | 1921 | ||
1913 | if (dm_table_supports_dax(t, PAGE_SIZE)) | 1922 | if (dm_table_supports_dax(t, device_supports_dax, &page_size)) { |
1914 | blk_queue_flag_set(QUEUE_FLAG_DAX, q); | 1923 | blk_queue_flag_set(QUEUE_FLAG_DAX, q); |
1924 | if (dm_table_supports_dax(t, device_synchronous, NULL)) | ||
1925 | set_dax_synchronous(t->md->dax_dev); | ||
1926 | } | ||
1915 | else | 1927 | else |
1916 | blk_queue_flag_clear(QUEUE_FLAG_DAX, q); | 1928 | blk_queue_flag_clear(QUEUE_FLAG_DAX, q); |
1917 | 1929 | ||
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 61f1152b74e9..d0beef033e2f 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -1117,7 +1117,7 @@ static bool dm_dax_supported(struct dax_device *dax_dev, struct block_device *bd | |||
1117 | if (!map) | 1117 | if (!map) |
1118 | return false; | 1118 | return false; |
1119 | 1119 | ||
1120 | ret = dm_table_supports_dax(map, blocksize); | 1120 | ret = dm_table_supports_dax(map, device_supports_dax, &blocksize); |
1121 | 1121 | ||
1122 | dm_put_live_table(md, srcu_idx); | 1122 | dm_put_live_table(md, srcu_idx); |
1123 | 1123 | ||
@@ -1989,7 +1989,8 @@ static struct mapped_device *alloc_dev(int minor) | |||
1989 | sprintf(md->disk->disk_name, "dm-%d", minor); | 1989 | sprintf(md->disk->disk_name, "dm-%d", minor); |
1990 | 1990 | ||
1991 | if (IS_ENABLED(CONFIG_DAX_DRIVER)) { | 1991 | if (IS_ENABLED(CONFIG_DAX_DRIVER)) { |
1992 | md->dax_dev = alloc_dax(md, md->disk->disk_name, &dm_dax_ops); | 1992 | md->dax_dev = alloc_dax(md, md->disk->disk_name, |
1993 | &dm_dax_ops, 0); | ||
1993 | if (!md->dax_dev) | 1994 | if (!md->dax_dev) |
1994 | goto bad; | 1995 | goto bad; |
1995 | } | 1996 | } |
diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 17e3db54404c..0475673337f3 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h | |||
@@ -72,7 +72,10 @@ bool dm_table_bio_based(struct dm_table *t); | |||
72 | bool dm_table_request_based(struct dm_table *t); | 72 | bool dm_table_request_based(struct dm_table *t); |
73 | void dm_table_free_md_mempools(struct dm_table *t); | 73 | void dm_table_free_md_mempools(struct dm_table *t); |
74 | struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t); | 74 | struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t); |
75 | bool dm_table_supports_dax(struct dm_table *t, int blocksize); | 75 | bool dm_table_supports_dax(struct dm_table *t, iterate_devices_callout_fn fn, |
76 | int *blocksize); | ||
77 | int device_supports_dax(struct dm_target *ti, struct dm_dev *dev, | ||
78 | sector_t start, sector_t len, void *data); | ||
76 | 79 | ||
77 | void dm_lock_md_type(struct mapped_device *md); | 80 | void dm_lock_md_type(struct mapped_device *md); |
78 | void dm_unlock_md_type(struct mapped_device *md); | 81 | void dm_unlock_md_type(struct mapped_device *md); |
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index 6f2a088afad6..cefe233e0b52 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_ND_BTT) += nd_btt.o | |||
5 | obj-$(CONFIG_ND_BLK) += nd_blk.o | 5 | obj-$(CONFIG_ND_BLK) += nd_blk.o |
6 | obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o | 6 | obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o |
7 | obj-$(CONFIG_OF_PMEM) += of_pmem.o | 7 | obj-$(CONFIG_OF_PMEM) += of_pmem.o |
8 | obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o | ||
8 | 9 | ||
9 | nd_pmem-y := pmem.o | 10 | nd_pmem-y := pmem.o |
10 | 11 | ||
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 26c1c7618891..2985ca949912 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c | |||
@@ -255,7 +255,7 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, | |||
255 | struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); | 255 | struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); |
256 | unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); | 256 | unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); |
257 | sector_t sector = offset >> 9; | 257 | sector_t sector = offset >> 9; |
258 | int rc = 0; | 258 | int rc = 0, ret = 0; |
259 | 259 | ||
260 | if (unlikely(!size)) | 260 | if (unlikely(!size)) |
261 | return 0; | 261 | return 0; |
@@ -293,7 +293,9 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, | |||
293 | } | 293 | } |
294 | 294 | ||
295 | memcpy_flushcache(nsio->addr + offset, buf, size); | 295 | memcpy_flushcache(nsio->addr + offset, buf, size); |
296 | nvdimm_flush(to_nd_region(ndns->dev.parent)); | 296 | ret = nvdimm_flush(to_nd_region(ndns->dev.parent), NULL); |
297 | if (ret) | ||
298 | rc = ret; | ||
297 | 299 | ||
298 | return rc; | 300 | return rc; |
299 | } | 301 | } |
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index a434a5964cb9..2d8d7e554877 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c | |||
@@ -1822,8 +1822,8 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, | |||
1822 | && !guid_equal(&nd_set->type_guid, | 1822 | && !guid_equal(&nd_set->type_guid, |
1823 | &nd_label->type_guid)) { | 1823 | &nd_label->type_guid)) { |
1824 | dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", | 1824 | dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", |
1825 | nd_set->type_guid.b, | 1825 | &nd_set->type_guid, |
1826 | nd_label->type_guid.b); | 1826 | &nd_label->type_guid); |
1827 | continue; | 1827 | continue; |
1828 | } | 1828 | } |
1829 | 1829 | ||
@@ -2227,8 +2227,8 @@ static struct device *create_namespace_blk(struct nd_region *nd_region, | |||
2227 | if (namespace_label_has(ndd, type_guid)) { | 2227 | if (namespace_label_has(ndd, type_guid)) { |
2228 | if (!guid_equal(&nd_set->type_guid, &nd_label->type_guid)) { | 2228 | if (!guid_equal(&nd_set->type_guid, &nd_label->type_guid)) { |
2229 | dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", | 2229 | dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", |
2230 | nd_set->type_guid.b, | 2230 | &nd_set->type_guid, |
2231 | nd_label->type_guid.b); | 2231 | &nd_label->type_guid); |
2232 | return ERR_PTR(-EAGAIN); | 2232 | return ERR_PTR(-EAGAIN); |
2233 | } | 2233 | } |
2234 | 2234 | ||
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index d24304c0e6d7..1b9955651379 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h | |||
@@ -155,6 +155,7 @@ struct nd_region { | |||
155 | struct badblocks bb; | 155 | struct badblocks bb; |
156 | struct nd_interleave_set *nd_set; | 156 | struct nd_interleave_set *nd_set; |
157 | struct nd_percpu_lane __percpu *lane; | 157 | struct nd_percpu_lane __percpu *lane; |
158 | int (*flush)(struct nd_region *nd_region, struct bio *bio); | ||
158 | struct nd_mapping mapping[0]; | 159 | struct nd_mapping mapping[0]; |
159 | }; | 160 | }; |
160 | 161 | ||
diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c new file mode 100644 index 000000000000..10351d5b49fa --- /dev/null +++ b/drivers/nvdimm/nd_virtio.c | |||
@@ -0,0 +1,125 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * virtio_pmem.c: Virtio pmem Driver | ||
4 | * | ||
5 | * Discovers persistent memory range information | ||
6 | * from host and provides a virtio based flushing | ||
7 | * interface. | ||
8 | */ | ||
9 | #include "virtio_pmem.h" | ||
10 | #include "nd.h" | ||
11 | |||
12 | /* The interrupt handler */ | ||
13 | void virtio_pmem_host_ack(struct virtqueue *vq) | ||
14 | { | ||
15 | struct virtio_pmem *vpmem = vq->vdev->priv; | ||
16 | struct virtio_pmem_request *req_data, *req_buf; | ||
17 | unsigned long flags; | ||
18 | unsigned int len; | ||
19 | |||
20 | spin_lock_irqsave(&vpmem->pmem_lock, flags); | ||
21 | while ((req_data = virtqueue_get_buf(vq, &len)) != NULL) { | ||
22 | req_data->done = true; | ||
23 | wake_up(&req_data->host_acked); | ||
24 | |||
25 | if (!list_empty(&vpmem->req_list)) { | ||
26 | req_buf = list_first_entry(&vpmem->req_list, | ||
27 | struct virtio_pmem_request, list); | ||
28 | req_buf->wq_buf_avail = true; | ||
29 | wake_up(&req_buf->wq_buf); | ||
30 | list_del(&req_buf->list); | ||
31 | } | ||
32 | } | ||
33 | spin_unlock_irqrestore(&vpmem->pmem_lock, flags); | ||
34 | } | ||
35 | EXPORT_SYMBOL_GPL(virtio_pmem_host_ack); | ||
36 | |||
37 | /* The request submission function */ | ||
38 | static int virtio_pmem_flush(struct nd_region *nd_region) | ||
39 | { | ||
40 | struct virtio_device *vdev = nd_region->provider_data; | ||
41 | struct virtio_pmem *vpmem = vdev->priv; | ||
42 | struct virtio_pmem_request *req_data; | ||
43 | struct scatterlist *sgs[2], sg, ret; | ||
44 | unsigned long flags; | ||
45 | int err, err1; | ||
46 | |||
47 | might_sleep(); | ||
48 | req_data = kmalloc(sizeof(*req_data), GFP_KERNEL); | ||
49 | if (!req_data) | ||
50 | return -ENOMEM; | ||
51 | |||
52 | req_data->done = false; | ||
53 | init_waitqueue_head(&req_data->host_acked); | ||
54 | init_waitqueue_head(&req_data->wq_buf); | ||
55 | INIT_LIST_HEAD(&req_data->list); | ||
56 | req_data->req.type = cpu_to_le32(VIRTIO_PMEM_REQ_TYPE_FLUSH); | ||
57 | sg_init_one(&sg, &req_data->req, sizeof(req_data->req)); | ||
58 | sgs[0] = &sg; | ||
59 | sg_init_one(&ret, &req_data->resp.ret, sizeof(req_data->resp)); | ||
60 | sgs[1] = &ret; | ||
61 | |||
62 | spin_lock_irqsave(&vpmem->pmem_lock, flags); | ||
63 | /* | ||
64 | * If virtqueue_add_sgs returns -ENOSPC then req_vq virtual | ||
65 | * queue does not have free descriptor. We add the request | ||
66 | * to req_list and wait for host_ack to wake us up when free | ||
67 | * slots are available. | ||
68 | */ | ||
69 | while ((err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data, | ||
70 | GFP_ATOMIC)) == -ENOSPC) { | ||
71 | |||
72 | dev_info(&vdev->dev, "failed to send command to virtio pmem device, no free slots in the virtqueue\n"); | ||
73 | req_data->wq_buf_avail = false; | ||
74 | list_add_tail(&req_data->list, &vpmem->req_list); | ||
75 | spin_unlock_irqrestore(&vpmem->pmem_lock, flags); | ||
76 | |||
77 | /* A host response results in "host_ack" getting called */ | ||
78 | wait_event(req_data->wq_buf, req_data->wq_buf_avail); | ||
79 | spin_lock_irqsave(&vpmem->pmem_lock, flags); | ||
80 | } | ||
81 | err1 = virtqueue_kick(vpmem->req_vq); | ||
82 | spin_unlock_irqrestore(&vpmem->pmem_lock, flags); | ||
83 | /* | ||
84 | * virtqueue_add_sgs failed with error different than -ENOSPC, we can't | ||
85 | * do anything about that. | ||
86 | */ | ||
87 | if (err || !err1) { | ||
88 | dev_info(&vdev->dev, "failed to send command to virtio pmem device\n"); | ||
89 | err = -EIO; | ||
90 | } else { | ||
91 | /* A host repsonse results in "host_ack" getting called */ | ||
92 | wait_event(req_data->host_acked, req_data->done); | ||
93 | err = le32_to_cpu(req_data->resp.ret); | ||
94 | } | ||
95 | |||
96 | kfree(req_data); | ||
97 | return err; | ||
98 | }; | ||
99 | |||
100 | /* The asynchronous flush callback function */ | ||
101 | int async_pmem_flush(struct nd_region *nd_region, struct bio *bio) | ||
102 | { | ||
103 | /* | ||
104 | * Create child bio for asynchronous flush and chain with | ||
105 | * parent bio. Otherwise directly call nd_region flush. | ||
106 | */ | ||
107 | if (bio && bio->bi_iter.bi_sector != -1) { | ||
108 | struct bio *child = bio_alloc(GFP_ATOMIC, 0); | ||
109 | |||
110 | if (!child) | ||
111 | return -ENOMEM; | ||
112 | bio_copy_dev(child, bio); | ||
113 | child->bi_opf = REQ_PREFLUSH; | ||
114 | child->bi_iter.bi_sector = -1; | ||
115 | bio_chain(child, bio); | ||
116 | submit_bio(child); | ||
117 | return 0; | ||
118 | } | ||
119 | if (virtio_pmem_flush(nd_region)) | ||
120 | return -EIO; | ||
121 | |||
122 | return 0; | ||
123 | }; | ||
124 | EXPORT_SYMBOL_GPL(async_pmem_flush); | ||
125 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index e7d8cc9f41e8..2bf3acd69613 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c | |||
@@ -184,6 +184,7 @@ static blk_status_t pmem_do_bvec(struct pmem_device *pmem, struct page *page, | |||
184 | 184 | ||
185 | static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) | 185 | static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) |
186 | { | 186 | { |
187 | int ret = 0; | ||
187 | blk_status_t rc = 0; | 188 | blk_status_t rc = 0; |
188 | bool do_acct; | 189 | bool do_acct; |
189 | unsigned long start; | 190 | unsigned long start; |
@@ -193,7 +194,7 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) | |||
193 | struct nd_region *nd_region = to_region(pmem); | 194 | struct nd_region *nd_region = to_region(pmem); |
194 | 195 | ||
195 | if (bio->bi_opf & REQ_PREFLUSH) | 196 | if (bio->bi_opf & REQ_PREFLUSH) |
196 | nvdimm_flush(nd_region); | 197 | ret = nvdimm_flush(nd_region, bio); |
197 | 198 | ||
198 | do_acct = nd_iostat_start(bio, &start); | 199 | do_acct = nd_iostat_start(bio, &start); |
199 | bio_for_each_segment(bvec, bio, iter) { | 200 | bio_for_each_segment(bvec, bio, iter) { |
@@ -208,7 +209,10 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) | |||
208 | nd_iostat_end(bio, start); | 209 | nd_iostat_end(bio, start); |
209 | 210 | ||
210 | if (bio->bi_opf & REQ_FUA) | 211 | if (bio->bi_opf & REQ_FUA) |
211 | nvdimm_flush(nd_region); | 212 | ret = nvdimm_flush(nd_region, bio); |
213 | |||
214 | if (ret) | ||
215 | bio->bi_status = errno_to_blk_status(ret); | ||
212 | 216 | ||
213 | bio_endio(bio); | 217 | bio_endio(bio); |
214 | return BLK_QC_T_NONE; | 218 | return BLK_QC_T_NONE; |
@@ -362,6 +366,7 @@ static int pmem_attach_disk(struct device *dev, | |||
362 | struct gendisk *disk; | 366 | struct gendisk *disk; |
363 | void *addr; | 367 | void *addr; |
364 | int rc; | 368 | int rc; |
369 | unsigned long flags = 0UL; | ||
365 | 370 | ||
366 | pmem = devm_kzalloc(dev, sizeof(*pmem), GFP_KERNEL); | 371 | pmem = devm_kzalloc(dev, sizeof(*pmem), GFP_KERNEL); |
367 | if (!pmem) | 372 | if (!pmem) |
@@ -457,14 +462,15 @@ static int pmem_attach_disk(struct device *dev, | |||
457 | nvdimm_badblocks_populate(nd_region, &pmem->bb, &bb_res); | 462 | nvdimm_badblocks_populate(nd_region, &pmem->bb, &bb_res); |
458 | disk->bb = &pmem->bb; | 463 | disk->bb = &pmem->bb; |
459 | 464 | ||
460 | dax_dev = alloc_dax(pmem, disk->disk_name, &pmem_dax_ops); | 465 | if (is_nvdimm_sync(nd_region)) |
466 | flags = DAXDEV_F_SYNC; | ||
467 | dax_dev = alloc_dax(pmem, disk->disk_name, &pmem_dax_ops, flags); | ||
461 | if (!dax_dev) { | 468 | if (!dax_dev) { |
462 | put_disk(disk); | 469 | put_disk(disk); |
463 | return -ENOMEM; | 470 | return -ENOMEM; |
464 | } | 471 | } |
465 | dax_write_cache(dax_dev, nvdimm_has_cache(nd_region)); | 472 | dax_write_cache(dax_dev, nvdimm_has_cache(nd_region)); |
466 | pmem->dax_dev = dax_dev; | 473 | pmem->dax_dev = dax_dev; |
467 | |||
468 | gendev = disk_to_dev(disk); | 474 | gendev = disk_to_dev(disk); |
469 | gendev->groups = pmem_attribute_groups; | 475 | gendev->groups = pmem_attribute_groups; |
470 | 476 | ||
@@ -522,14 +528,14 @@ static int nd_pmem_remove(struct device *dev) | |||
522 | sysfs_put(pmem->bb_state); | 528 | sysfs_put(pmem->bb_state); |
523 | pmem->bb_state = NULL; | 529 | pmem->bb_state = NULL; |
524 | } | 530 | } |
525 | nvdimm_flush(to_nd_region(dev->parent)); | 531 | nvdimm_flush(to_nd_region(dev->parent), NULL); |
526 | 532 | ||
527 | return 0; | 533 | return 0; |
528 | } | 534 | } |
529 | 535 | ||
530 | static void nd_pmem_shutdown(struct device *dev) | 536 | static void nd_pmem_shutdown(struct device *dev) |
531 | { | 537 | { |
532 | nvdimm_flush(to_nd_region(dev->parent)); | 538 | nvdimm_flush(to_nd_region(dev->parent), NULL); |
533 | } | 539 | } |
534 | 540 | ||
535 | static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) | 541 | static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) |
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 4fed9ce9c2fe..56f2227f192a 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c | |||
@@ -287,7 +287,9 @@ static ssize_t deep_flush_store(struct device *dev, struct device_attribute *att | |||
287 | return rc; | 287 | return rc; |
288 | if (!flush) | 288 | if (!flush) |
289 | return -EINVAL; | 289 | return -EINVAL; |
290 | nvdimm_flush(nd_region); | 290 | rc = nvdimm_flush(nd_region, NULL); |
291 | if (rc) | ||
292 | return rc; | ||
291 | 293 | ||
292 | return len; | 294 | return len; |
293 | } | 295 | } |
@@ -1077,6 +1079,11 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, | |||
1077 | dev->of_node = ndr_desc->of_node; | 1079 | dev->of_node = ndr_desc->of_node; |
1078 | nd_region->ndr_size = resource_size(ndr_desc->res); | 1080 | nd_region->ndr_size = resource_size(ndr_desc->res); |
1079 | nd_region->ndr_start = ndr_desc->res->start; | 1081 | nd_region->ndr_start = ndr_desc->res->start; |
1082 | if (ndr_desc->flush) | ||
1083 | nd_region->flush = ndr_desc->flush; | ||
1084 | else | ||
1085 | nd_region->flush = NULL; | ||
1086 | |||
1080 | nd_device_register(dev); | 1087 | nd_device_register(dev); |
1081 | 1088 | ||
1082 | return nd_region; | 1089 | return nd_region; |
@@ -1117,11 +1124,24 @@ struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus, | |||
1117 | } | 1124 | } |
1118 | EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create); | 1125 | EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create); |
1119 | 1126 | ||
1127 | int nvdimm_flush(struct nd_region *nd_region, struct bio *bio) | ||
1128 | { | ||
1129 | int rc = 0; | ||
1130 | |||
1131 | if (!nd_region->flush) | ||
1132 | rc = generic_nvdimm_flush(nd_region); | ||
1133 | else { | ||
1134 | if (nd_region->flush(nd_region, bio)) | ||
1135 | rc = -EIO; | ||
1136 | } | ||
1137 | |||
1138 | return rc; | ||
1139 | } | ||
1120 | /** | 1140 | /** |
1121 | * nvdimm_flush - flush any posted write queues between the cpu and pmem media | 1141 | * nvdimm_flush - flush any posted write queues between the cpu and pmem media |
1122 | * @nd_region: blk or interleaved pmem region | 1142 | * @nd_region: blk or interleaved pmem region |
1123 | */ | 1143 | */ |
1124 | void nvdimm_flush(struct nd_region *nd_region) | 1144 | int generic_nvdimm_flush(struct nd_region *nd_region) |
1125 | { | 1145 | { |
1126 | struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev); | 1146 | struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev); |
1127 | int i, idx; | 1147 | int i, idx; |
@@ -1145,6 +1165,8 @@ void nvdimm_flush(struct nd_region *nd_region) | |||
1145 | if (ndrd_get_flush_wpq(ndrd, i, 0)) | 1165 | if (ndrd_get_flush_wpq(ndrd, i, 0)) |
1146 | writeq(1, ndrd_get_flush_wpq(ndrd, i, idx)); | 1166 | writeq(1, ndrd_get_flush_wpq(ndrd, i, idx)); |
1147 | wmb(); | 1167 | wmb(); |
1168 | |||
1169 | return 0; | ||
1148 | } | 1170 | } |
1149 | EXPORT_SYMBOL_GPL(nvdimm_flush); | 1171 | EXPORT_SYMBOL_GPL(nvdimm_flush); |
1150 | 1172 | ||
@@ -1189,6 +1211,13 @@ int nvdimm_has_cache(struct nd_region *nd_region) | |||
1189 | } | 1211 | } |
1190 | EXPORT_SYMBOL_GPL(nvdimm_has_cache); | 1212 | EXPORT_SYMBOL_GPL(nvdimm_has_cache); |
1191 | 1213 | ||
1214 | bool is_nvdimm_sync(struct nd_region *nd_region) | ||
1215 | { | ||
1216 | return is_nd_pmem(&nd_region->dev) && | ||
1217 | !test_bit(ND_REGION_ASYNC, &nd_region->flags); | ||
1218 | } | ||
1219 | EXPORT_SYMBOL_GPL(is_nvdimm_sync); | ||
1220 | |||
1192 | struct conflict_context { | 1221 | struct conflict_context { |
1193 | struct nd_region *nd_region; | 1222 | struct nd_region *nd_region; |
1194 | resource_size_t start, size; | 1223 | resource_size_t start, size; |
diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c new file mode 100644 index 000000000000..5e3d07b47e0c --- /dev/null +++ b/drivers/nvdimm/virtio_pmem.c | |||
@@ -0,0 +1,122 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * virtio_pmem.c: Virtio pmem Driver | ||
4 | * | ||
5 | * Discovers persistent memory range information | ||
6 | * from host and registers the virtual pmem device | ||
7 | * with libnvdimm core. | ||
8 | */ | ||
9 | #include "virtio_pmem.h" | ||
10 | #include "nd.h" | ||
11 | |||
12 | static struct virtio_device_id id_table[] = { | ||
13 | { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID }, | ||
14 | { 0 }, | ||
15 | }; | ||
16 | |||
17 | /* Initialize virt queue */ | ||
18 | static int init_vq(struct virtio_pmem *vpmem) | ||
19 | { | ||
20 | /* single vq */ | ||
21 | vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, | ||
22 | virtio_pmem_host_ack, "flush_queue"); | ||
23 | if (IS_ERR(vpmem->req_vq)) | ||
24 | return PTR_ERR(vpmem->req_vq); | ||
25 | |||
26 | spin_lock_init(&vpmem->pmem_lock); | ||
27 | INIT_LIST_HEAD(&vpmem->req_list); | ||
28 | |||
29 | return 0; | ||
30 | }; | ||
31 | |||
32 | static int virtio_pmem_probe(struct virtio_device *vdev) | ||
33 | { | ||
34 | struct nd_region_desc ndr_desc = {}; | ||
35 | int nid = dev_to_node(&vdev->dev); | ||
36 | struct nd_region *nd_region; | ||
37 | struct virtio_pmem *vpmem; | ||
38 | struct resource res; | ||
39 | int err = 0; | ||
40 | |||
41 | if (!vdev->config->get) { | ||
42 | dev_err(&vdev->dev, "%s failure: config access disabled\n", | ||
43 | __func__); | ||
44 | return -EINVAL; | ||
45 | } | ||
46 | |||
47 | vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); | ||
48 | if (!vpmem) { | ||
49 | err = -ENOMEM; | ||
50 | goto out_err; | ||
51 | } | ||
52 | |||
53 | vpmem->vdev = vdev; | ||
54 | vdev->priv = vpmem; | ||
55 | err = init_vq(vpmem); | ||
56 | if (err) { | ||
57 | dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); | ||
58 | goto out_err; | ||
59 | } | ||
60 | |||
61 | virtio_cread(vpmem->vdev, struct virtio_pmem_config, | ||
62 | start, &vpmem->start); | ||
63 | virtio_cread(vpmem->vdev, struct virtio_pmem_config, | ||
64 | size, &vpmem->size); | ||
65 | |||
66 | res.start = vpmem->start; | ||
67 | res.end = vpmem->start + vpmem->size - 1; | ||
68 | vpmem->nd_desc.provider_name = "virtio-pmem"; | ||
69 | vpmem->nd_desc.module = THIS_MODULE; | ||
70 | |||
71 | vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, | ||
72 | &vpmem->nd_desc); | ||
73 | if (!vpmem->nvdimm_bus) { | ||
74 | dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); | ||
75 | err = -ENXIO; | ||
76 | goto out_vq; | ||
77 | } | ||
78 | |||
79 | dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); | ||
80 | |||
81 | ndr_desc.res = &res; | ||
82 | ndr_desc.numa_node = nid; | ||
83 | ndr_desc.flush = async_pmem_flush; | ||
84 | set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); | ||
85 | set_bit(ND_REGION_ASYNC, &ndr_desc.flags); | ||
86 | nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); | ||
87 | if (!nd_region) { | ||
88 | dev_err(&vdev->dev, "failed to create nvdimm region\n"); | ||
89 | err = -ENXIO; | ||
90 | goto out_nd; | ||
91 | } | ||
92 | nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); | ||
93 | return 0; | ||
94 | out_nd: | ||
95 | nvdimm_bus_unregister(vpmem->nvdimm_bus); | ||
96 | out_vq: | ||
97 | vdev->config->del_vqs(vdev); | ||
98 | out_err: | ||
99 | return err; | ||
100 | } | ||
101 | |||
102 | static void virtio_pmem_remove(struct virtio_device *vdev) | ||
103 | { | ||
104 | struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); | ||
105 | |||
106 | nvdimm_bus_unregister(nvdimm_bus); | ||
107 | vdev->config->del_vqs(vdev); | ||
108 | vdev->config->reset(vdev); | ||
109 | } | ||
110 | |||
111 | static struct virtio_driver virtio_pmem_driver = { | ||
112 | .driver.name = KBUILD_MODNAME, | ||
113 | .driver.owner = THIS_MODULE, | ||
114 | .id_table = id_table, | ||
115 | .probe = virtio_pmem_probe, | ||
116 | .remove = virtio_pmem_remove, | ||
117 | }; | ||
118 | |||
119 | module_virtio_driver(virtio_pmem_driver); | ||
120 | MODULE_DEVICE_TABLE(virtio, id_table); | ||
121 | MODULE_DESCRIPTION("Virtio pmem driver"); | ||
122 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/nvdimm/virtio_pmem.h b/drivers/nvdimm/virtio_pmem.h new file mode 100644 index 000000000000..0dddefe594c4 --- /dev/null +++ b/drivers/nvdimm/virtio_pmem.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * virtio_pmem.h: virtio pmem Driver | ||
4 | * | ||
5 | * Discovers persistent memory range information | ||
6 | * from host and provides a virtio based flushing | ||
7 | * interface. | ||
8 | **/ | ||
9 | |||
10 | #ifndef _LINUX_VIRTIO_PMEM_H | ||
11 | #define _LINUX_VIRTIO_PMEM_H | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <uapi/linux/virtio_pmem.h> | ||
15 | #include <linux/libnvdimm.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | |||
18 | struct virtio_pmem_request { | ||
19 | struct virtio_pmem_req req; | ||
20 | struct virtio_pmem_resp resp; | ||
21 | |||
22 | /* Wait queue to process deferred work after ack from host */ | ||
23 | wait_queue_head_t host_acked; | ||
24 | bool done; | ||
25 | |||
26 | /* Wait queue to process deferred work after virt queue buffer avail */ | ||
27 | wait_queue_head_t wq_buf; | ||
28 | bool wq_buf_avail; | ||
29 | struct list_head list; | ||
30 | }; | ||
31 | |||
32 | struct virtio_pmem { | ||
33 | struct virtio_device *vdev; | ||
34 | |||
35 | /* Virtio pmem request queue */ | ||
36 | struct virtqueue *req_vq; | ||
37 | |||
38 | /* nvdimm bus registers virtio pmem device */ | ||
39 | struct nvdimm_bus *nvdimm_bus; | ||
40 | struct nvdimm_bus_descriptor nd_desc; | ||
41 | |||
42 | /* List to store deferred work if virtqueue is full */ | ||
43 | struct list_head req_list; | ||
44 | |||
45 | /* Synchronize virtqueue data */ | ||
46 | spinlock_t pmem_lock; | ||
47 | |||
48 | /* Memory region information */ | ||
49 | __u64 start; | ||
50 | __u64 size; | ||
51 | }; | ||
52 | |||
53 | void virtio_pmem_host_ack(struct virtqueue *vq); | ||
54 | int async_pmem_flush(struct nd_region *nd_region, struct bio *bio); | ||
55 | #endif | ||
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index d04d4378ca50..63502ca537eb 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c | |||
@@ -679,7 +679,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
679 | goto put_dev; | 679 | goto put_dev; |
680 | 680 | ||
681 | dev_info->dax_dev = alloc_dax(dev_info, dev_info->gd->disk_name, | 681 | dev_info->dax_dev = alloc_dax(dev_info, dev_info->gd->disk_name, |
682 | &dcssblk_dax_ops); | 682 | &dcssblk_dax_ops, DAXDEV_F_SYNC); |
683 | if (!dev_info->dax_dev) { | 683 | if (!dev_info->dax_dev) { |
684 | rc = -ENOMEM; | 684 | rc = -ENOMEM; |
685 | goto put_dev; | 685 | goto put_dev; |
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 023fc3bc01c6..078615cf2afc 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig | |||
@@ -43,6 +43,17 @@ config VIRTIO_PCI_LEGACY | |||
43 | 43 | ||
44 | If unsure, say Y. | 44 | If unsure, say Y. |
45 | 45 | ||
46 | config VIRTIO_PMEM | ||
47 | tristate "Support for virtio pmem driver" | ||
48 | depends on VIRTIO | ||
49 | depends on LIBNVDIMM | ||
50 | help | ||
51 | This driver provides access to virtio-pmem devices, storage devices | ||
52 | that are mapped into the physical address space - similar to NVDIMMs | ||
53 | - with a virtio-based flushing interface. | ||
54 | |||
55 | If unsure, say Y. | ||
56 | |||
46 | config VIRTIO_BALLOON | 57 | config VIRTIO_BALLOON |
47 | tristate "Virtio balloon driver" | 58 | tristate "Virtio balloon driver" |
48 | depends on VIRTIO | 59 | depends on VIRTIO |
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index f4a24a46245e..70b0438dbc94 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
@@ -371,15 +371,17 @@ static const struct vm_operations_struct ext4_file_vm_ops = { | |||
371 | static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) | 371 | static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) |
372 | { | 372 | { |
373 | struct inode *inode = file->f_mapping->host; | 373 | struct inode *inode = file->f_mapping->host; |
374 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
375 | struct dax_device *dax_dev = sbi->s_daxdev; | ||
374 | 376 | ||
375 | if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) | 377 | if (unlikely(ext4_forced_shutdown(sbi))) |
376 | return -EIO; | 378 | return -EIO; |
377 | 379 | ||
378 | /* | 380 | /* |
379 | * We don't support synchronous mappings for non-DAX files. At least | 381 | * We don't support synchronous mappings for non-DAX files and |
380 | * until someone comes with a sensible use case. | 382 | * for DAX files if underneath dax_device is not synchronous. |
381 | */ | 383 | */ |
382 | if (!IS_DAX(file_inode(file)) && (vma->vm_flags & VM_SYNC)) | 384 | if (!daxdev_mapping_supported(vma, dax_dev)) |
383 | return -EOPNOTSUPP; | 385 | return -EOPNOTSUPP; |
384 | 386 | ||
385 | file_accessed(file); | 387 | file_accessed(file); |
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index e93bacbd49ae..28101bbc0b78 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c | |||
@@ -1197,11 +1197,14 @@ xfs_file_mmap( | |||
1197 | struct file *filp, | 1197 | struct file *filp, |
1198 | struct vm_area_struct *vma) | 1198 | struct vm_area_struct *vma) |
1199 | { | 1199 | { |
1200 | struct dax_device *dax_dev; | ||
1201 | |||
1202 | dax_dev = xfs_find_daxdev_for_inode(file_inode(filp)); | ||
1200 | /* | 1203 | /* |
1201 | * We don't support synchronous mappings for non-DAX files. At least | 1204 | * We don't support synchronous mappings for non-DAX files and |
1202 | * until someone comes with a sensible use case. | 1205 | * for DAX files if underneath dax_device is not synchronous. |
1203 | */ | 1206 | */ |
1204 | if (!IS_DAX(file_inode(filp)) && (vma->vm_flags & VM_SYNC)) | 1207 | if (!daxdev_mapping_supported(vma, dax_dev)) |
1205 | return -EOPNOTSUPP; | 1208 | return -EOPNOTSUPP; |
1206 | 1209 | ||
1207 | file_accessed(filp); | 1210 | file_accessed(filp); |
diff --git a/include/linux/dax.h b/include/linux/dax.h index becaea5f4488..9bd8528bd305 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h | |||
@@ -7,6 +7,9 @@ | |||
7 | #include <linux/radix-tree.h> | 7 | #include <linux/radix-tree.h> |
8 | #include <asm/pgtable.h> | 8 | #include <asm/pgtable.h> |
9 | 9 | ||
10 | /* Flag for synchronous flush */ | ||
11 | #define DAXDEV_F_SYNC (1UL << 0) | ||
12 | |||
10 | typedef unsigned long dax_entry_t; | 13 | typedef unsigned long dax_entry_t; |
11 | 14 | ||
12 | struct iomap_ops; | 15 | struct iomap_ops; |
@@ -38,18 +41,40 @@ extern struct attribute_group dax_attribute_group; | |||
38 | #if IS_ENABLED(CONFIG_DAX) | 41 | #if IS_ENABLED(CONFIG_DAX) |
39 | struct dax_device *dax_get_by_host(const char *host); | 42 | struct dax_device *dax_get_by_host(const char *host); |
40 | struct dax_device *alloc_dax(void *private, const char *host, | 43 | struct dax_device *alloc_dax(void *private, const char *host, |
41 | const struct dax_operations *ops); | 44 | const struct dax_operations *ops, unsigned long flags); |
42 | void put_dax(struct dax_device *dax_dev); | 45 | void put_dax(struct dax_device *dax_dev); |
43 | void kill_dax(struct dax_device *dax_dev); | 46 | void kill_dax(struct dax_device *dax_dev); |
44 | void dax_write_cache(struct dax_device *dax_dev, bool wc); | 47 | void dax_write_cache(struct dax_device *dax_dev, bool wc); |
45 | bool dax_write_cache_enabled(struct dax_device *dax_dev); | 48 | bool dax_write_cache_enabled(struct dax_device *dax_dev); |
49 | bool __dax_synchronous(struct dax_device *dax_dev); | ||
50 | static inline bool dax_synchronous(struct dax_device *dax_dev) | ||
51 | { | ||
52 | return __dax_synchronous(dax_dev); | ||
53 | } | ||
54 | void __set_dax_synchronous(struct dax_device *dax_dev); | ||
55 | static inline void set_dax_synchronous(struct dax_device *dax_dev) | ||
56 | { | ||
57 | __set_dax_synchronous(dax_dev); | ||
58 | } | ||
59 | /* | ||
60 | * Check if given mapping is supported by the file / underlying device. | ||
61 | */ | ||
62 | static inline bool daxdev_mapping_supported(struct vm_area_struct *vma, | ||
63 | struct dax_device *dax_dev) | ||
64 | { | ||
65 | if (!(vma->vm_flags & VM_SYNC)) | ||
66 | return true; | ||
67 | if (!IS_DAX(file_inode(vma->vm_file))) | ||
68 | return false; | ||
69 | return dax_synchronous(dax_dev); | ||
70 | } | ||
46 | #else | 71 | #else |
47 | static inline struct dax_device *dax_get_by_host(const char *host) | 72 | static inline struct dax_device *dax_get_by_host(const char *host) |
48 | { | 73 | { |
49 | return NULL; | 74 | return NULL; |
50 | } | 75 | } |
51 | static inline struct dax_device *alloc_dax(void *private, const char *host, | 76 | static inline struct dax_device *alloc_dax(void *private, const char *host, |
52 | const struct dax_operations *ops) | 77 | const struct dax_operations *ops, unsigned long flags) |
53 | { | 78 | { |
54 | /* | 79 | /* |
55 | * Callers should check IS_ENABLED(CONFIG_DAX) to know if this | 80 | * Callers should check IS_ENABLED(CONFIG_DAX) to know if this |
@@ -70,6 +95,18 @@ static inline bool dax_write_cache_enabled(struct dax_device *dax_dev) | |||
70 | { | 95 | { |
71 | return false; | 96 | return false; |
72 | } | 97 | } |
98 | static inline bool dax_synchronous(struct dax_device *dax_dev) | ||
99 | { | ||
100 | return true; | ||
101 | } | ||
102 | static inline void set_dax_synchronous(struct dax_device *dax_dev) | ||
103 | { | ||
104 | } | ||
105 | static inline bool daxdev_mapping_supported(struct vm_area_struct *vma, | ||
106 | struct dax_device *dax_dev) | ||
107 | { | ||
108 | return !(vma->vm_flags & VM_SYNC); | ||
109 | } | ||
73 | #endif | 110 | #endif |
74 | 111 | ||
75 | struct writeback_control; | 112 | struct writeback_control; |
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 03d5c3aece9d..7a64b3ddb408 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <linux/uuid.h> | 12 | #include <linux/uuid.h> |
13 | #include <linux/spinlock.h> | 13 | #include <linux/spinlock.h> |
14 | #include <linux/bio.h> | ||
14 | 15 | ||
15 | struct badrange_entry { | 16 | struct badrange_entry { |
16 | u64 start; | 17 | u64 start; |
@@ -57,6 +58,9 @@ enum { | |||
57 | */ | 58 | */ |
58 | ND_REGION_PERSIST_MEMCTRL = 2, | 59 | ND_REGION_PERSIST_MEMCTRL = 2, |
59 | 60 | ||
61 | /* Platform provides asynchronous flush mechanism */ | ||
62 | ND_REGION_ASYNC = 3, | ||
63 | |||
60 | /* mark newly adjusted resources as requiring a label update */ | 64 | /* mark newly adjusted resources as requiring a label update */ |
61 | DPA_RESOURCE_ADJUSTED = 1 << 0, | 65 | DPA_RESOURCE_ADJUSTED = 1 << 0, |
62 | }; | 66 | }; |
@@ -113,6 +117,7 @@ struct nd_mapping_desc { | |||
113 | int position; | 117 | int position; |
114 | }; | 118 | }; |
115 | 119 | ||
120 | struct nd_region; | ||
116 | struct nd_region_desc { | 121 | struct nd_region_desc { |
117 | struct resource *res; | 122 | struct resource *res; |
118 | struct nd_mapping_desc *mapping; | 123 | struct nd_mapping_desc *mapping; |
@@ -125,6 +130,7 @@ struct nd_region_desc { | |||
125 | int target_node; | 130 | int target_node; |
126 | unsigned long flags; | 131 | unsigned long flags; |
127 | struct device_node *of_node; | 132 | struct device_node *of_node; |
133 | int (*flush)(struct nd_region *nd_region, struct bio *bio); | ||
128 | }; | 134 | }; |
129 | 135 | ||
130 | struct device; | 136 | struct device; |
@@ -252,10 +258,12 @@ unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr); | |||
252 | unsigned int nd_region_acquire_lane(struct nd_region *nd_region); | 258 | unsigned int nd_region_acquire_lane(struct nd_region *nd_region); |
253 | void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane); | 259 | void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane); |
254 | u64 nd_fletcher64(void *addr, size_t len, bool le); | 260 | u64 nd_fletcher64(void *addr, size_t len, bool le); |
255 | void nvdimm_flush(struct nd_region *nd_region); | 261 | int nvdimm_flush(struct nd_region *nd_region, struct bio *bio); |
262 | int generic_nvdimm_flush(struct nd_region *nd_region); | ||
256 | int nvdimm_has_flush(struct nd_region *nd_region); | 263 | int nvdimm_has_flush(struct nd_region *nd_region); |
257 | int nvdimm_has_cache(struct nd_region *nd_region); | 264 | int nvdimm_has_cache(struct nd_region *nd_region); |
258 | int nvdimm_in_overwrite(struct nvdimm *nvdimm); | 265 | int nvdimm_in_overwrite(struct nvdimm *nvdimm); |
266 | bool is_nvdimm_sync(struct nd_region *nd_region); | ||
259 | 267 | ||
260 | static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf, | 268 | static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
261 | unsigned int buf_len, int *cmd_rc) | 269 | unsigned int buf_len, int *cmd_rc) |
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index cfe47c5d9a56..348fd0176f75 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h | |||
@@ -44,5 +44,6 @@ | |||
44 | #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ | 44 | #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ |
45 | #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ | 45 | #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ |
46 | #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ | 46 | #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ |
47 | #define VIRTIO_ID_PMEM 27 /* virtio pmem */ | ||
47 | 48 | ||
48 | #endif /* _LINUX_VIRTIO_IDS_H */ | 49 | #endif /* _LINUX_VIRTIO_IDS_H */ |
diff --git a/include/uapi/linux/virtio_pmem.h b/include/uapi/linux/virtio_pmem.h new file mode 100644 index 000000000000..9a63ed6d062f --- /dev/null +++ b/include/uapi/linux/virtio_pmem.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ | ||
2 | /* | ||
3 | * Definitions for virtio-pmem devices. | ||
4 | * | ||
5 | * Copyright (C) 2019 Red Hat, Inc. | ||
6 | * | ||
7 | * Author(s): Pankaj Gupta <pagupta@redhat.com> | ||
8 | */ | ||
9 | |||
10 | #ifndef _UAPI_LINUX_VIRTIO_PMEM_H | ||
11 | #define _UAPI_LINUX_VIRTIO_PMEM_H | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | #include <linux/virtio_ids.h> | ||
15 | #include <linux/virtio_config.h> | ||
16 | |||
17 | struct virtio_pmem_config { | ||
18 | __u64 start; | ||
19 | __u64 size; | ||
20 | }; | ||
21 | |||
22 | #define VIRTIO_PMEM_REQ_TYPE_FLUSH 0 | ||
23 | |||
24 | struct virtio_pmem_resp { | ||
25 | /* Host return status corresponding to flush request */ | ||
26 | __le32 ret; | ||
27 | }; | ||
28 | |||
29 | struct virtio_pmem_req { | ||
30 | /* command type */ | ||
31 | __le32 type; | ||
32 | }; | ||
33 | |||
34 | #endif | ||