Commit 730926c3 authored by Dan Williams's avatar Dan Williams

device-dax: Add /sys/class/dax backwards compatibility

On the expectation that some environments may not upgrade libdaxctl
(userspace component that depends on the /sys/class/dax hierarchy),
provide a default / legacy dax_pmem_compat driver. The dax_pmem_compat
driver implements the original /sys/class/dax sysfs layout rather than
/sys/bus/dax. When userspace is upgraded it can blacklist this module
and switch to the dax_pmem driver going forward.

CONFIG_DEV_DAX_PMEM_COMPAT and supporting code will be deleted according
to the dax_pmem entry in Documentation/ABI/obsolete/.
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent d200781e
What: /sys/class/dax/
Date: May, 2016
KernelVersion: v4.7
Contact: linux-nvdimm@lists.01.org
Description: Device DAX is the device-centric analogue of Filesystem
DAX (CONFIG_FS_DAX). It allows memory ranges to be
allocated and mapped without need of an intervening file
system. Device DAX is strict, precise and predictable.
Specifically this interface:
1/ Guarantees fault granularity with respect to a given
page size (pte, pmd, or pud) set at configuration time.
2/ Enforces deterministic behavior by being strict about
what fault scenarios are supported.
The /sys/class/dax/ interface enumerates all the
device-dax instances in the system. The ABI is
deprecated and will be removed after 2020. It is
replaced with the DAX bus interface /sys/bus/dax/ where
device-dax instances can be found under
/sys/bus/dax/devices/
...@@ -23,12 +23,22 @@ config DEV_DAX ...@@ -23,12 +23,22 @@ config DEV_DAX
config DEV_DAX_PMEM config DEV_DAX_PMEM
tristate "PMEM DAX: direct access to persistent memory" tristate "PMEM DAX: direct access to persistent memory"
depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX
depends on m # until we can kill DEV_DAX_PMEM_COMPAT
default DEV_DAX default DEV_DAX
help help
Support raw access to persistent memory. Note that this Support raw access to persistent memory. Note that this
driver consumes memory ranges allocated and exported by the driver consumes memory ranges allocated and exported by the
libnvdimm sub-system. libnvdimm sub-system.
Say Y if unsure Say M if unsure
config DEV_DAX_PMEM_COMPAT
tristate "PMEM DAX: support the deprecated /sys/class/dax interface"
depends on DEV_DAX_PMEM
default DEV_DAX_PMEM
help
Older versions of the libdaxctl library expect to find all
device-dax instances under /sys/class/dax. If libdaxctl in
your distribution is older than v58 say M, otherwise say N.
endif endif
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DAX) += dax.o obj-$(CONFIG_DAX) += dax.o
obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
dax-y := super.o dax-y := super.o
dax-y += bus.o dax-y += bus.o
dax_pmem-y := pmem.o
device_dax-y := device.o device_dax-y := device.o
obj-y += pmem/
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "dax-private.h" #include "dax-private.h"
#include "bus.h" #include "bus.h"
static struct class *dax_class;
static DEFINE_MUTEX(dax_bus_lock); static DEFINE_MUTEX(dax_bus_lock);
#define DAX_NAME_LEN 30 #define DAX_NAME_LEN 30
...@@ -310,8 +312,8 @@ static void unregister_dev_dax(void *dev) ...@@ -310,8 +312,8 @@ static void unregister_dev_dax(void *dev)
put_device(dev); put_device(dev);
} }
struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id,
struct dev_pagemap *pgmap) struct dev_pagemap *pgmap, enum dev_dax_subsys subsys)
{ {
struct device *parent = dax_region->dev; struct device *parent = dax_region->dev;
struct dax_device *dax_dev; struct dax_device *dax_dev;
...@@ -350,7 +352,10 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, ...@@ -350,7 +352,10 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id,
inode = dax_inode(dax_dev); inode = dax_inode(dax_dev);
dev->devt = inode->i_rdev; dev->devt = inode->i_rdev;
dev->bus = &dax_bus_type; if (subsys == DEV_DAX_BUS)
dev->bus = &dax_bus_type;
else
dev->class = dax_class;
dev->parent = parent; dev->parent = parent;
dev->groups = dax_attribute_groups; dev->groups = dax_attribute_groups;
dev->release = dev_dax_release; dev->release = dev_dax_release;
...@@ -374,7 +379,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, ...@@ -374,7 +379,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id,
return ERR_PTR(rc); return ERR_PTR(rc);
} }
EXPORT_SYMBOL_GPL(devm_create_dev_dax); EXPORT_SYMBOL_GPL(__devm_create_dev_dax);
static int match_always_count; static int match_always_count;
...@@ -407,6 +412,7 @@ EXPORT_SYMBOL_GPL(__dax_driver_register); ...@@ -407,6 +412,7 @@ EXPORT_SYMBOL_GPL(__dax_driver_register);
void dax_driver_unregister(struct dax_device_driver *dax_drv) void dax_driver_unregister(struct dax_device_driver *dax_drv)
{ {
struct device_driver *drv = &dax_drv->drv;
struct dax_id *dax_id, *_id; struct dax_id *dax_id, *_id;
mutex_lock(&dax_bus_lock); mutex_lock(&dax_bus_lock);
...@@ -416,15 +422,28 @@ void dax_driver_unregister(struct dax_device_driver *dax_drv) ...@@ -416,15 +422,28 @@ void dax_driver_unregister(struct dax_device_driver *dax_drv)
kfree(dax_id); kfree(dax_id);
} }
mutex_unlock(&dax_bus_lock); mutex_unlock(&dax_bus_lock);
driver_unregister(drv);
} }
EXPORT_SYMBOL_GPL(dax_driver_unregister); EXPORT_SYMBOL_GPL(dax_driver_unregister);
int __init dax_bus_init(void) int __init dax_bus_init(void)
{ {
return bus_register(&dax_bus_type); int rc;
if (IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)) {
dax_class = class_create(THIS_MODULE, "dax");
if (IS_ERR(dax_class))
return PTR_ERR(dax_class);
}
rc = bus_register(&dax_bus_type);
if (rc)
class_destroy(dax_class);
return rc;
} }
void __exit dax_bus_exit(void) void __exit dax_bus_exit(void)
{ {
bus_unregister(&dax_bus_type); bus_unregister(&dax_bus_type);
class_destroy(dax_class);
} }
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ /* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
#ifndef __DAX_BUS_H__ #ifndef __DAX_BUS_H__
#define __DAX_BUS_H__ #define __DAX_BUS_H__
struct device; #include <linux/device.h>
struct dev_dax; struct dev_dax;
struct resource; struct resource;
struct dax_device; struct dax_device;
...@@ -10,8 +11,23 @@ struct dax_region; ...@@ -10,8 +11,23 @@ struct dax_region;
void dax_region_put(struct dax_region *dax_region); void dax_region_put(struct dax_region *dax_region);
struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct dax_region *alloc_dax_region(struct device *parent, int region_id,
struct resource *res, unsigned int align, unsigned long flags); struct resource *res, unsigned int align, unsigned long flags);
struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id,
struct dev_pagemap *pgmap); enum dev_dax_subsys {
DEV_DAX_BUS,
DEV_DAX_CLASS,
};
struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id,
struct dev_pagemap *pgmap, enum dev_dax_subsys subsys);
static inline struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region,
int id, struct dev_pagemap *pgmap)
{
return __devm_create_dev_dax(dax_region, id, pgmap, DEV_DAX_BUS);
}
/* to be deleted when DEV_DAX_CLASS is removed */
struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys);
struct dax_device_driver { struct dax_device_driver {
struct device_driver drv; struct device_driver drv;
...@@ -26,6 +42,10 @@ int __dax_driver_register(struct dax_device_driver *dax_drv, ...@@ -26,6 +42,10 @@ int __dax_driver_register(struct dax_device_driver *dax_drv,
void dax_driver_unregister(struct dax_device_driver *dax_drv); void dax_driver_unregister(struct dax_device_driver *dax_drv);
void kill_dev_dax(struct dev_dax *dev_dax); void kill_dev_dax(struct dev_dax *dev_dax);
#if IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)
int dev_dax_probe(struct device *dev);
#endif
/* /*
* While run_dax() is potentially a generic operation that could be * While run_dax() is potentially a generic operation that could be
* defined in include/linux/dax.h we don't want to grow any users * defined in include/linux/dax.h we don't want to grow any users
......
...@@ -445,7 +445,7 @@ static void dev_dax_kill(void *dev_dax) ...@@ -445,7 +445,7 @@ static void dev_dax_kill(void *dev_dax)
kill_dev_dax(dev_dax); kill_dev_dax(dev_dax);
} }
static int dev_dax_probe(struct device *dev) int dev_dax_probe(struct device *dev)
{ {
struct dev_dax *dev_dax = to_dev_dax(dev); struct dev_dax *dev_dax = to_dev_dax(dev);
struct dax_device *dax_dev = dev_dax->dax_dev; struct dax_device *dax_dev = dev_dax->dax_dev;
...@@ -484,7 +484,11 @@ static int dev_dax_probe(struct device *dev) ...@@ -484,7 +484,11 @@ static int dev_dax_probe(struct device *dev)
inode = dax_inode(dax_dev); inode = dax_inode(dax_dev);
cdev = inode->i_cdev; cdev = inode->i_cdev;
cdev_init(cdev, &dax_fops); cdev_init(cdev, &dax_fops);
cdev->owner = dev->driver->owner; if (dev->class) {
/* for the CONFIG_DEV_DAX_PMEM_COMPAT case */
cdev->owner = dev->parent->driver->owner;
} else
cdev->owner = dev->driver->owner;
cdev_set_parent(cdev, &dev->kobj); cdev_set_parent(cdev, &dev->kobj);
rc = cdev_add(cdev, dev->devt, 1); rc = cdev_add(cdev, dev->devt, 1);
if (rc) if (rc)
...@@ -497,6 +501,7 @@ static int dev_dax_probe(struct device *dev) ...@@ -497,6 +501,7 @@ static int dev_dax_probe(struct device *dev)
run_dax(dax_dev); run_dax(dax_dev);
return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax); return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax);
} }
EXPORT_SYMBOL_GPL(dev_dax_probe);
static int dev_dax_remove(struct device *dev) static int dev_dax_remove(struct device *dev)
{ {
......
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o
obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o
dax_pmem-y := pmem.o
dax_pmem_core-y := core.o
dax_pmem_compat-y := compat.o
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
#include <linux/percpu-refcount.h>
#include <linux/memremap.h>
#include <linux/module.h>
#include <linux/pfn_t.h>
#include <linux/nd.h>
#include "../bus.h"
/* we need the private definitions to implement compat suport */
#include "../dax-private.h"
static int dax_pmem_compat_probe(struct device *dev)
{
struct dev_dax *dev_dax = __dax_pmem_probe(dev, DEV_DAX_CLASS);
int rc;
if (IS_ERR(dev_dax))
return PTR_ERR(dev_dax);
if (!devres_open_group(&dev_dax->dev, dev_dax, GFP_KERNEL))
return -ENOMEM;
device_lock(&dev_dax->dev);
rc = dev_dax_probe(&dev_dax->dev);
device_unlock(&dev_dax->dev);
devres_close_group(&dev_dax->dev, dev_dax);
if (rc)
devres_release_group(&dev_dax->dev, dev_dax);
return rc;
}
static int dax_pmem_compat_release(struct device *dev, void *data)
{
device_lock(dev);
devres_release_group(dev, to_dev_dax(dev));
device_unlock(dev);
return 0;
}
static int dax_pmem_compat_remove(struct device *dev)
{
device_for_each_child(dev, NULL, dax_pmem_compat_release);
return 0;
}
static struct nd_device_driver dax_pmem_compat_driver = {
.probe = dax_pmem_compat_probe,
.remove = dax_pmem_compat_remove,
.drv = {
.name = "dax_pmem_compat",
},
.type = ND_DRIVER_DAX_PMEM,
};
static int __init dax_pmem_compat_init(void)
{
return nd_driver_register(&dax_pmem_compat_driver);
}
module_init(dax_pmem_compat_init);
static void __exit dax_pmem_compat_exit(void)
{
driver_unregister(&dax_pmem_compat_driver.drv);
}
module_exit(dax_pmem_compat_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation");
MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
/* // SPDX-License-Identifier: GPL-2.0
* Copyright(c) 2016 Intel Corporation. All rights reserved. /* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/percpu-refcount.h>
#include <linux/memremap.h> #include <linux/memremap.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pfn_t.h> #include <linux/pfn_t.h>
#include "../nvdimm/pfn.h" #include "../../nvdimm/pfn.h"
#include "../nvdimm/nd.h" #include "../../nvdimm/nd.h"
#include "bus.h" #include "../bus.h"
static int dax_pmem_probe(struct device *dev) struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys)
{ {
struct resource res; struct resource res;
int rc, id, region_id; int rc, id, region_id;
...@@ -34,16 +23,16 @@ static int dax_pmem_probe(struct device *dev) ...@@ -34,16 +23,16 @@ static int dax_pmem_probe(struct device *dev)
ndns = nvdimm_namespace_common_probe(dev); ndns = nvdimm_namespace_common_probe(dev);
if (IS_ERR(ndns)) if (IS_ERR(ndns))
return PTR_ERR(ndns); return ERR_CAST(ndns);
nsio = to_nd_namespace_io(&ndns->dev); nsio = to_nd_namespace_io(&ndns->dev);
/* parse the 'pfn' info block via ->rw_bytes */ /* parse the 'pfn' info block via ->rw_bytes */
rc = devm_nsio_enable(dev, nsio); rc = devm_nsio_enable(dev, nsio);
if (rc) if (rc)
return rc; return ERR_PTR(rc);
rc = nvdimm_setup_pfn(nd_pfn, &pgmap); rc = nvdimm_setup_pfn(nd_pfn, &pgmap);
if (rc) if (rc)
return rc; return ERR_PTR(rc);
devm_nsio_disable(dev, nsio); devm_nsio_disable(dev, nsio);
/* reserve the metadata area, device-dax will reserve the data */ /* reserve the metadata area, device-dax will reserve the data */
...@@ -52,12 +41,12 @@ static int dax_pmem_probe(struct device *dev) ...@@ -52,12 +41,12 @@ static int dax_pmem_probe(struct device *dev)
if (!devm_request_mem_region(dev, nsio->res.start, offset, if (!devm_request_mem_region(dev, nsio->res.start, offset,
dev_name(&ndns->dev))) { dev_name(&ndns->dev))) {
dev_warn(dev, "could not reserve metadata\n"); dev_warn(dev, "could not reserve metadata\n");
return -EBUSY; return ERR_PTR(-EBUSY);
} }
rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", &region_id, &id); rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", &region_id, &id);
if (rc != 2) if (rc != 2)
return -EINVAL; return ERR_PTR(-EINVAL);
/* adjust the dax_region resource to the start of data */ /* adjust the dax_region resource to the start of data */
memcpy(&res, &pgmap.res, sizeof(res)); memcpy(&res, &pgmap.res, sizeof(res));
...@@ -65,26 +54,16 @@ static int dax_pmem_probe(struct device *dev) ...@@ -65,26 +54,16 @@ static int dax_pmem_probe(struct device *dev)
dax_region = alloc_dax_region(dev, region_id, &res, dax_region = alloc_dax_region(dev, region_id, &res,
le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP); le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP);
if (!dax_region) if (!dax_region)
return -ENOMEM; return ERR_PTR(-ENOMEM);
dev_dax = devm_create_dev_dax(dax_region, id, &pgmap); dev_dax = __devm_create_dev_dax(dax_region, id, &pgmap, subsys);
/* child dev_dax instances now own the lifetime of the dax_region */ /* child dev_dax instances now own the lifetime of the dax_region */
dax_region_put(dax_region); dax_region_put(dax_region);
return PTR_ERR_OR_ZERO(dev_dax); return dev_dax;
} }
EXPORT_SYMBOL_GPL(__dax_pmem_probe);
static struct nd_device_driver dax_pmem_driver = {
.probe = dax_pmem_probe,
.drv = {
.name = "dax_pmem",
},
.type = ND_DRIVER_DAX_PMEM,
};
module_nd_driver(dax_pmem_driver);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation"); MODULE_AUTHOR("Intel Corporation");
MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
#include <linux/percpu-refcount.h>
#include <linux/memremap.h>
#include <linux/module.h>
#include <linux/pfn_t.h>
#include <linux/nd.h>
#include "../bus.h"
static int dax_pmem_probe(struct device *dev)
{
return PTR_ERR_OR_ZERO(__dax_pmem_probe(dev, DEV_DAX_BUS));
}
static struct nd_device_driver dax_pmem_driver = {
.probe = dax_pmem_probe,
.drv = {
.name = "dax_pmem",
},
.type = ND_DRIVER_DAX_PMEM,
};
static int __init dax_pmem_init(void)
{
return nd_driver_register(&dax_pmem_driver);
}
module_init(dax_pmem_init);
static void __exit dax_pmem_exit(void)
{
driver_unregister(&dax_pmem_driver.drv);
}
module_exit(dax_pmem_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation");
#if !IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)
/* For compat builds, don't load this module by default */
MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
#endif
...@@ -35,6 +35,8 @@ obj-$(CONFIG_DAX) += dax.o ...@@ -35,6 +35,8 @@ obj-$(CONFIG_DAX) += dax.o
endif endif
obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o
obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o
nfit-y := $(ACPI_SRC)/core.o nfit-y := $(ACPI_SRC)/core.o
nfit-y += $(ACPI_SRC)/intel.o nfit-y += $(ACPI_SRC)/intel.o
...@@ -65,7 +67,9 @@ device_dax-y += dax-dev.o ...@@ -65,7 +67,9 @@ device_dax-y += dax-dev.o
device_dax-y += device_dax_test.o device_dax-y += device_dax_test.o
device_dax-y += config_check.o device_dax-y += config_check.o
dax_pmem-y := $(DAX_SRC)/pmem.o dax_pmem-y := $(DAX_SRC)/pmem/pmem.o
dax_pmem_core-y := $(DAX_SRC)/pmem/core.o
dax_pmem_compat-y := $(DAX_SRC)/pmem/compat.o
dax_pmem-y += config_check.o dax_pmem-y += config_check.o
libnvdimm-y := $(NVDIMM_SRC)/core.o libnvdimm-y := $(NVDIMM_SRC)/core.o
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment