Merge remote-tracking branch 'nvdimm/libnvdimm-for-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 03:29:36 +0000 (13:29 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 03:29:36 +0000 (13:29 +1000)
17 files changed:
drivers/acpi/nfit/core.c
drivers/acpi/nfit/nfit.h
drivers/dax/Kconfig
drivers/dax/dax.c
drivers/dax/dax.h
drivers/dax/pmem.c
drivers/nvdimm/dimm.c
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/namespace_devs.c
drivers/nvdimm/nd.h
fs/char_dev.c
include/linux/libnvdimm.h
include/uapi/linux/magic.h
include/uapi/linux/ndctl.h
tools/testing/nvdimm/Kbuild
tools/testing/nvdimm/test/iomap.c
tools/testing/nvdimm/test/nfit.c

index 80cc7c089a15e908ae070d95ad4879e5b6309555..ceb6671ab35536862135c93ba049e60d65562d9f 100644 (file)
@@ -1248,6 +1248,44 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
        return NULL;
 }
 
+void __acpi_nvdimm_notify(struct device *dev, u32 event)
+{
+       struct nfit_mem *nfit_mem;
+       struct acpi_nfit_desc *acpi_desc;
+
+       dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__,
+                       event);
+
+       if (event != NFIT_NOTIFY_DIMM_HEALTH) {
+               dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev),
+                               event);
+               return;
+       }
+
+       acpi_desc = dev_get_drvdata(dev->parent);
+       if (!acpi_desc)
+               return;
+
+       /*
+        * If we successfully retrieved acpi_desc, then we know nfit_mem data
+        * is still valid.
+        */
+       nfit_mem = dev_get_drvdata(dev);
+       if (nfit_mem && nfit_mem->flags_attr)
+               sysfs_notify_dirent(nfit_mem->flags_attr);
+}
+EXPORT_SYMBOL_GPL(__acpi_nvdimm_notify);
+
+static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct acpi_device *adev = data;
+       struct device *dev = &adev->dev;
+
+       device_lock(dev->parent);
+       __acpi_nvdimm_notify(dev, event);
+       device_unlock(dev->parent);
+}
+
 static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
                struct nfit_mem *nfit_mem, u32 device_handle)
 {
@@ -1272,6 +1310,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
                return force_enable_dimms ? 0 : -ENODEV;
        }
 
+       if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle,
+               ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) {
+               dev_err(dev, "%s: notification registration failed\n",
+                               dev_name(&adev_dimm->dev));
+               return -ENXIO;
+       }
+
        /*
         * Until standardization materializes we need to consider 4
         * different command sets.  Note, that checking for function0 (bit0)
@@ -1310,18 +1355,41 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
        return 0;
 }
 
+static void shutdown_dimm_notify(void *data)
+{
+       struct acpi_nfit_desc *acpi_desc = data;
+       struct nfit_mem *nfit_mem;
+
+       mutex_lock(&acpi_desc->init_mutex);
+       /*
+        * Clear out the nfit_mem->flags_attr and shut down dimm event
+        * notifications.
+        */
+       list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+               struct acpi_device *adev_dimm = nfit_mem->adev;
+
+               if (nfit_mem->flags_attr) {
+                       sysfs_put(nfit_mem->flags_attr);
+                       nfit_mem->flags_attr = NULL;
+               }
+               if (adev_dimm)
+                       acpi_remove_notify_handler(adev_dimm->handle,
+                                       ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify);
+       }
+       mutex_unlock(&acpi_desc->init_mutex);
+}
+
 static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
 {
        struct nfit_mem *nfit_mem;
-       int dimm_count = 0;
+       int dimm_count = 0, rc;
+       struct nvdimm *nvdimm;
 
        list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
                struct acpi_nfit_flush_address *flush;
                unsigned long flags = 0, cmd_mask;
-               struct nvdimm *nvdimm;
                u32 device_handle;
                u16 mem_flags;
-               int rc;
 
                device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
                nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
@@ -1374,7 +1442,30 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
 
        }
 
-       return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
+       rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
+       if (rc)
+               return rc;
+
+       /*
+        * Now that dimms are successfully registered, and async registration
+        * is flushed, attempt to enable event notification.
+        */
+       list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+               struct kernfs_node *nfit_kernfs;
+
+               nvdimm = nfit_mem->nvdimm;
+               nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
+               if (nfit_kernfs)
+                       nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
+                                       "flags");
+               sysfs_put(nfit_kernfs);
+               if (!nfit_mem->flags_attr)
+                       dev_warn(acpi_desc->dev, "%s: notifications disabled\n",
+                                       nvdimm_name(nvdimm));
+       }
+
+       return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify,
+                       acpi_desc);
 }
 
 static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
@@ -2670,29 +2761,30 @@ static int acpi_nfit_remove(struct acpi_device *adev)
        return 0;
 }
 
-static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
+void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
 {
-       struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev);
+       struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
-       struct device *dev = &adev->dev;
        union acpi_object *obj;
        acpi_status status;
        int ret;
 
        dev_dbg(dev, "%s: event: %d\n", __func__, event);
 
-       device_lock(dev);
+       if (event != NFIT_NOTIFY_UPDATE)
+               return;
+
        if (!dev->driver) {
                /* dev->driver may be null if we're being removed */
                dev_dbg(dev, "%s: no driver found for dev\n", __func__);
-               goto out_unlock;
+               return;
        }
 
        if (!acpi_desc) {
                acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
                if (!acpi_desc)
-                       goto out_unlock;
-               acpi_nfit_desc_init(acpi_desc, &adev->dev);
+                       return;
+               acpi_nfit_desc_init(acpi_desc, dev);
        } else {
                /*
                 * Finish previous registration before considering new
@@ -2702,10 +2794,10 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
        }
 
        /* Evaluate _FIT */
-       status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
+       status = acpi_evaluate_object(handle, "_FIT", NULL, &buf);
        if (ACPI_FAILURE(status)) {
                dev_err(dev, "failed to evaluate _FIT\n");
-               goto out_unlock;
+               return;
        }
 
        obj = buf.pointer;
@@ -2717,9 +2809,14 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
        } else
                dev_err(dev, "Invalid _FIT\n");
        kfree(buf.pointer);
+}
+EXPORT_SYMBOL_GPL(__acpi_nfit_notify);
 
- out_unlock:
-       device_unlock(dev);
+static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
+{
+       device_lock(&adev->dev);
+       __acpi_nfit_notify(&adev->dev, adev->handle, event);
+       device_unlock(&adev->dev);
 }
 
 static const struct acpi_device_id acpi_nfit_ids[] = {
index e894ded24d99399483dc593e24cdd277d8393db3..bb101170cd0b383d5878965f1fe7b144b87d4151 100644 (file)
@@ -78,6 +78,14 @@ enum {
        NFIT_ARS_TIMEOUT = 90,
 };
 
+enum nfit_root_notifiers {
+       NFIT_NOTIFY_UPDATE = 0x80,
+};
+
+enum nfit_dimm_notifiers {
+       NFIT_NOTIFY_DIMM_HEALTH = 0x81,
+};
+
 struct nfit_spa {
        struct list_head list;
        struct nd_region *nd_region;
@@ -124,6 +132,7 @@ struct nfit_mem {
        struct acpi_nfit_system_address *spa_bdw;
        struct acpi_nfit_interleave *idt_dcr;
        struct acpi_nfit_interleave *idt_bdw;
+       struct kernfs_node *flags_attr;
        struct nfit_flush *nfit_flush;
        struct list_head list;
        struct acpi_device *adev;
@@ -223,5 +232,7 @@ static inline struct acpi_nfit_desc *to_acpi_desc(
 
 const u8 *to_nfit_uuid(enum nfit_uuids id);
 int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *nfit, acpi_size sz);
+void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event);
+void __acpi_nvdimm_notify(struct device *dev, u32 event);
 void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev);
 #endif /* __NFIT_H__ */
index cedab7572de3b49fcba213daae52c789000b87f5..daadd20aa936a85e087ba0145147ffa48eac05a9 100644 (file)
@@ -23,4 +23,9 @@ config DEV_DAX_PMEM
 
          Say Y if unsure
 
+config NR_DEV_DAX
+       int "Maximum number of Device-DAX instances"
+       default 32768
+       range 256 2147483647
+
 endif
index 29f600f2c447159e84b17bc43bdcd66ca8bbcc7b..ed4843a9fb42b193feffb1dba2421a28fbab2e33 100644 (file)
 #include <linux/pagemap.h>
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/mount.h>
 #include <linux/pfn_t.h>
+#include <linux/hash.h>
+#include <linux/cdev.h>
 #include <linux/slab.h>
 #include <linux/dax.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
+#include "dax.h"
 
-static int dax_major;
+static dev_t dax_devt;
 static struct class *dax_class;
 static DEFINE_IDA(dax_minor_ida);
+static int nr_dax = CONFIG_NR_DEV_DAX;
+module_param(nr_dax, int, S_IRUGO);
+static struct vfsmount *dax_mnt;
+static struct kmem_cache *dax_cache __read_mostly;
+static struct super_block *dax_superblock __read_mostly;
+MODULE_PARM_DESC(nr_dax, "max number of device-dax instances");
 
 /**
  * struct dax_region - mapping infrastructure for dax devices
  * @id: kernel-wide unique region for a memory range
  * @base: linear address corresponding to @res
  * @kref: to pin while other agents have a need to do lookups
+ * @lock: synchronize changes / consistent-access to the resource tree (@res)
  * @dev: parent device backing this region
+ * @seed: next device for dynamic allocation / configuration
  * @align: allocation and mapping alignment for child dax devices
  * @res: physical address range of the region
+ * @child_count: number of registered dax device instances
  * @pfn_flags: identify whether the pfns are paged back or not
  */
 struct dax_region {
@@ -38,9 +51,12 @@ struct dax_region {
        struct ida ida;
        void *base;
        struct kref kref;
+       struct mutex lock;
        struct device *dev;
+       struct device *seed;
        unsigned int align;
        struct resource res;
+       atomic_t child_count;
        unsigned long pfn_flags;
 };
 
@@ -48,7 +64,7 @@ struct dax_region {
  * struct dax_dev - subdivision of a dax region
  * @region - parent region
  * @dev - device backing the character device
- * @kref - enable this data to be tracked in filp->private_data
+ * @cdev - core chardev data
  * @alive - !alive + rcu grace period == no new mappings can be established
  * @id - child id in the region
  * @num_resources - number of physical address extents in this device
@@ -56,268 +72,303 @@ struct dax_region {
  */
 struct dax_dev {
        struct dax_region *region;
-       struct device *dev;
-       struct kref kref;
+       struct inode *inode;
+       struct device dev;
+       struct cdev cdev;
        bool alive;
        int id;
        int num_resources;
-       struct resource res[0];
+       struct resource **res;
 };
 
-static void dax_region_free(struct kref *kref)
-{
-       struct dax_region *dax_region;
-
-       dax_region = container_of(kref, struct dax_region, kref);
-       kfree(dax_region);
-}
-
-void dax_region_put(struct dax_region *dax_region)
-{
-       kref_put(&dax_region->kref, dax_region_free);
-}
-EXPORT_SYMBOL_GPL(dax_region_put);
+#define for_each_dax_region_resource(dax_region, res) \
+       for (res = (dax_region)->res.child; res; res = res->sibling)
 
-static void dax_dev_free(struct kref *kref)
+static unsigned long long dax_region_avail_size(
+               struct dax_region *dax_region)
 {
-       struct dax_dev *dax_dev;
+       unsigned long long size;
+       struct resource *res;
 
-       dax_dev = container_of(kref, struct dax_dev, kref);
-       dax_region_put(dax_dev->region);
-       kfree(dax_dev);
-}
+       mutex_lock(&dax_region->lock);
+       size = resource_size(&dax_region->res);
+       for_each_dax_region_resource(dax_region, res)
+               size -= resource_size(res);
+       mutex_unlock(&dax_region->lock);
 
-static void dax_dev_put(struct dax_dev *dax_dev)
-{
-       kref_put(&dax_dev->kref, dax_dev_free);
+       return size;
 }
 
-struct dax_region *alloc_dax_region(struct device *parent, int region_id,
-               struct resource *res, unsigned int align, void *addr,
-               unsigned long pfn_flags)
+static ssize_t available_size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
 {
        struct dax_region *dax_region;
+       ssize_t rc = -ENXIO;
 
-       dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL);
-
-       if (!dax_region)
-               return NULL;
-
-       memcpy(&dax_region->res, res, sizeof(*res));
-       dax_region->pfn_flags = pfn_flags;
-       kref_init(&dax_region->kref);
-       dax_region->id = region_id;
-       ida_init(&dax_region->ida);
-       dax_region->align = align;
-       dax_region->dev = parent;
-       dax_region->base = addr;
+       device_lock(dev);
+       dax_region = dev_get_drvdata(dev);
+       if (dax_region)
+               rc = sprintf(buf, "%llu\n", dax_region_avail_size(dax_region));
+       device_unlock(dev);
 
-       return dax_region;
+       return rc;
 }
-EXPORT_SYMBOL_GPL(alloc_dax_region);
+static DEVICE_ATTR_RO(available_size);
 
-static ssize_t size_show(struct device *dev,
+static ssize_t seed_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct dax_dev *dax_dev = dev_get_drvdata(dev);
-       unsigned long long size = 0;
-       int i;
+       struct dax_region *dax_region;
+       ssize_t rc = -ENXIO;
 
-       for (i = 0; i < dax_dev->num_resources; i++)
-               size += resource_size(&dax_dev->res[i]);
+       device_lock(dev);
+       dax_region = dev_get_drvdata(dev);
+       if (dax_region) {
+               mutex_lock(&dax_region->lock);
+               if (dax_region->seed)
+                       rc = sprintf(buf, "%s\n", dev_name(dax_region->seed));
+               mutex_unlock(&dax_region->lock);
+       }
+       device_unlock(dev);
 
-       return sprintf(buf, "%llu\n", size);
+       return rc;
 }
-static DEVICE_ATTR_RO(size);
+static DEVICE_ATTR_RO(seed);
 
-static struct attribute *dax_device_attributes[] = {
-       &dev_attr_size.attr,
+static struct attribute *dax_region_attributes[] = {
+       &dev_attr_available_size.attr,
+       &dev_attr_seed.attr,
        NULL,
 };
 
-static const struct attribute_group dax_device_attribute_group = {
-       .attrs = dax_device_attributes,
+static const struct attribute_group dax_region_attribute_group = {
+       .name = "dax_region",
+       .attrs = dax_region_attributes,
 };
 
-static const struct attribute_group *dax_attribute_groups[] = {
-       &dax_device_attribute_group,
+static const struct attribute_group *dax_region_attribute_groups[] = {
+       &dax_region_attribute_group,
        NULL,
 };
 
-static void unregister_dax_dev(void *_dev)
+static struct inode *dax_alloc_inode(struct super_block *sb)
 {
-       struct device *dev = _dev;
-       struct dax_dev *dax_dev = dev_get_drvdata(dev);
-       struct dax_region *dax_region = dax_dev->region;
+       return kmem_cache_alloc(dax_cache, GFP_KERNEL);
+}
 
-       dev_dbg(dev, "%s\n", __func__);
+static void dax_i_callback(struct rcu_head *head)
+{
+       struct inode *inode = container_of(head, struct inode, i_rcu);
 
-       /*
-        * Note, rcu is not protecting the liveness of dax_dev, rcu is
-        * ensuring that any fault handlers that might have seen
-        * dax_dev->alive == true, have completed.  Any fault handlers
-        * that start after synchronize_rcu() has started will abort
-        * upon seeing dax_dev->alive == false.
-        */
-       dax_dev->alive = false;
-       synchronize_rcu();
+       kmem_cache_free(dax_cache, inode);
+}
 
-       get_device(dev);
-       device_unregister(dev);
-       ida_simple_remove(&dax_region->ida, dax_dev->id);
-       ida_simple_remove(&dax_minor_ida, MINOR(dev->devt));
-       put_device(dev);
-       dax_dev_put(dax_dev);
+static void dax_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, dax_i_callback);
 }
 
-int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res,
-               int count)
+static const struct super_operations dax_sops = {
+       .statfs = simple_statfs,
+       .alloc_inode = dax_alloc_inode,
+       .destroy_inode = dax_destroy_inode,
+       .drop_inode = generic_delete_inode,
+};
+
+static struct dentry *dax_mount(struct file_system_type *fs_type,
+               int flags, const char *dev_name, void *data)
 {
-       struct device *parent = dax_region->dev;
-       struct dax_dev *dax_dev;
-       struct device *dev;
-       int rc, minor;
-       dev_t dev_t;
+       return mount_pseudo(fs_type, "dax:", &dax_sops, NULL, DAXFS_MAGIC);
+}
 
-       dax_dev = kzalloc(sizeof(*dax_dev) + sizeof(*res) * count, GFP_KERNEL);
-       if (!dax_dev)
-               return -ENOMEM;
-       memcpy(dax_dev->res, res, sizeof(*res) * count);
-       dax_dev->num_resources = count;
-       kref_init(&dax_dev->kref);
-       dax_dev->alive = true;
-       dax_dev->region = dax_region;
-       kref_get(&dax_region->kref);
+static struct file_system_type dax_type = {
+       .name = "dax",
+       .mount = dax_mount,
+       .kill_sb = kill_anon_super,
+};
 
-       dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
-       if (dax_dev->id < 0) {
-               rc = dax_dev->id;
-               goto err_id;
-       }
+static int dax_test(struct inode *inode, void *data)
+{
+       return inode->i_cdev == data;
+}
 
-       minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL);
-       if (minor < 0) {
-               rc = minor;
-               goto err_minor;
-       }
+static int dax_set(struct inode *inode, void *data)
+{
+       inode->i_cdev = data;
+       return 0;
+}
 
-       dev_t = MKDEV(dax_major, minor);
-       dev = device_create_with_groups(dax_class, parent, dev_t, dax_dev,
-                       dax_attribute_groups, "dax%d.%d", dax_region->id,
-                       dax_dev->id);
-       if (IS_ERR(dev)) {
-               rc = PTR_ERR(dev);
-               goto err_create;
-       }
-       dax_dev->dev = dev;
+static struct inode *dax_inode_get(struct cdev *cdev, dev_t devt)
+{
+       struct inode *inode;
 
-       rc = devm_add_action_or_reset(dax_region->dev, unregister_dax_dev, dev);
-       if (rc)
-               return rc;
+       inode = iget5_locked(dax_superblock, hash_32(devt + DAXFS_MAGIC, 31),
+                       dax_test, dax_set, cdev);
 
-       return 0;
+       if (!inode)
+               return NULL;
 
- err_create:
-       ida_simple_remove(&dax_minor_ida, minor);
- err_minor:
-       ida_simple_remove(&dax_region->ida, dax_dev->id);
- err_id:
-       dax_dev_put(dax_dev);
+       if (inode->i_state & I_NEW) {
+               inode->i_mode = S_IFCHR;
+               inode->i_flags = S_DAX;
+               inode->i_rdev = devt;
+               mapping_set_gfp_mask(&inode->i_data, GFP_USER);
+               unlock_new_inode(inode);
+       }
+       return inode;
+}
 
-       return rc;
+static void init_once(void *inode)
+{
+       inode_init_once(inode);
 }
-EXPORT_SYMBOL_GPL(devm_create_dax_dev);
 
-/* return an unmapped area aligned to the dax region specified alignment */
-static unsigned long dax_dev_get_unmapped_area(struct file *filp,
-               unsigned long addr, unsigned long len, unsigned long pgoff,
-               unsigned long flags)
+static int dax_inode_init(void)
 {
-       unsigned long off, off_end, off_align, len_align, addr_align, align;
-       struct dax_dev *dax_dev = filp ? filp->private_data : NULL;
-       struct dax_region *dax_region;
+       int rc;
 
-       if (!dax_dev || addr)
-               goto out;
+       dax_cache = kmem_cache_create("dax_cache", sizeof(struct inode), 0,
+                       (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
+                        SLAB_MEM_SPREAD|SLAB_ACCOUNT),
+                       init_once);
+       if (!dax_cache)
+               return -ENOMEM;
 
-       dax_region = dax_dev->region;
-       align = dax_region->align;
-       off = pgoff << PAGE_SHIFT;
-       off_end = off + len;
-       off_align = round_up(off, align);
+       rc = register_filesystem(&dax_type);
+       if (rc)
+               goto err_register_fs;
 
-       if ((off_end <= off_align) || ((off_end - off_align) < align))
-               goto out;
+       dax_mnt = kern_mount(&dax_type);
+       if (IS_ERR(dax_mnt)) {
+               rc = PTR_ERR(dax_mnt);
+               goto err_mount;
+       }
+       dax_superblock = dax_mnt->mnt_sb;
 
-       len_align = len + align;
-       if ((off + len_align) < off)
-               goto out;
+       return 0;
 
-       addr_align = current->mm->get_unmapped_area(filp, addr, len_align,
-                       pgoff, flags);
-       if (!IS_ERR_VALUE(addr_align)) {
-               addr_align += (off - addr_align) & (align - 1);
-               return addr_align;
-       }
- out:
-       return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags);
+ err_mount:
+       unregister_filesystem(&dax_type);
+ err_register_fs:
+       kmem_cache_destroy(dax_cache);
+
+       return rc;
 }
 
-static int __match_devt(struct device *dev, const void *data)
+static void dax_inode_exit(void)
 {
-       const dev_t *devt = data;
+       kern_unmount(dax_mnt);
+       unregister_filesystem(&dax_type);
+       kmem_cache_destroy(dax_cache);
+}
 
-       return dev->devt == *devt;
+static void dax_region_free(struct kref *kref)
+{
+       struct dax_region *dax_region;
+
+       dax_region = container_of(kref, struct dax_region, kref);
+       WARN(atomic_read(&dax_region->child_count),
+                       "%s: child count not zero\n",
+                       dev_name(dax_region->dev));
+       kfree(dax_region);
 }
 
-static struct device *dax_dev_find(dev_t dev_t)
+void dax_region_put(struct dax_region *dax_region)
 {
-       return class_find_device(dax_class, NULL, &dev_t, __match_devt);
+       kref_put(&dax_region->kref, dax_region_free);
 }
+EXPORT_SYMBOL_GPL(dax_region_put);
+
 
-static int dax_dev_open(struct inode *inode, struct file *filp)
+static void dax_region_unregister(void *region)
 {
-       struct dax_dev *dax_dev = NULL;
-       struct device *dev;
+       struct dax_region *dax_region = region;
 
-       dev = dax_dev_find(inode->i_rdev);
-       if (!dev)
-               return -ENXIO;
+       sysfs_remove_groups(&dax_region->dev->kobj,
+                       dax_region_attribute_groups);
+       dax_region_put(dax_region);
+}
 
-       device_lock(dev);
-       dax_dev = dev_get_drvdata(dev);
-       if (dax_dev) {
-               dev_dbg(dev, "%s\n", __func__);
-               filp->private_data = dax_dev;
-               kref_get(&dax_dev->kref);
-               inode->i_flags = S_DAX;
+struct dax_region *alloc_dax_region(struct device *parent, int region_id,
+               struct resource *res, unsigned int align, void *addr,
+               unsigned long pfn_flags)
+{
+       struct dax_region *dax_region;
+
+       if (dev_get_drvdata(parent)) {
+               dev_WARN(parent, "dax core found drvdata already in use\n");
+               return NULL;
        }
-       device_unlock(dev);
 
-       if (!dax_dev) {
-               put_device(dev);
-               return -ENXIO;
+       if (!IS_ALIGNED(res->start, align)
+                       || !IS_ALIGNED(resource_size(res), align))
+               return NULL;
+
+       dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL);
+       if (!dax_region)
+               return NULL;
+       dev_set_drvdata(parent, dax_region);
+       dax_region->res.name = dev_name(parent);
+       dax_region->res.start = res->start;
+       dax_region->res.end = res->end;
+       dax_region->pfn_flags = pfn_flags;
+       mutex_init(&dax_region->lock);
+       kref_init(&dax_region->kref);
+       dax_region->id = region_id;
+       ida_init(&dax_region->ida);
+       dax_region->align = align;
+       dax_region->dev = parent;
+       dax_region->base = addr;
+       if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
+               kfree(dax_region);
+               return NULL;;
        }
-       return 0;
+
+       kref_get(&dax_region->kref);
+       if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region))
+               return NULL;
+       return dax_region;
+}
+EXPORT_SYMBOL_GPL(alloc_dax_region);
+
+static struct dax_dev *to_dax_dev(struct device *dev)
+{
+       return container_of(dev, struct dax_dev, dev);
 }
 
-static int dax_dev_release(struct inode *inode, struct file *filp)
+static ssize_t size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
 {
-       struct dax_dev *dax_dev = filp->private_data;
-       struct device *dev = dax_dev->dev;
+       struct dax_dev *dax_dev = to_dax_dev(dev);
+       unsigned long long size = 0;
+       int i;
 
-       dev_dbg(dax_dev->dev, "%s\n", __func__);
-       dax_dev_put(dax_dev);
-       put_device(dev);
+       for (i = 0; i < dax_dev->num_resources; i++)
+               size += resource_size(dax_dev->res[i]);
 
-       return 0;
+       return sprintf(buf, "%llu\n", size);
 }
+static DEVICE_ATTR_RO(size);
+
+static struct attribute *dax_device_attributes[] = {
+       &dev_attr_size.attr,
+       NULL,
+};
+
+static const struct attribute_group dax_device_attribute_group = {
+       .attrs = dax_device_attributes,
+};
+
+static const struct attribute_group *dax_attribute_groups[] = {
+       &dax_device_attribute_group,
+       NULL,
+};
 
 static int check_vma(struct dax_dev *dax_dev, struct vm_area_struct *vma,
                const char *func)
 {
        struct dax_region *dax_region = dax_dev->region;
-       struct device *dev = dax_dev->dev;
+       struct device *dev = &dax_dev->dev;
        unsigned long mask;
 
        if (!dax_dev->alive)
@@ -362,7 +413,7 @@ static phys_addr_t pgoff_to_phys(struct dax_dev *dax_dev, pgoff_t pgoff,
        int i;
 
        for (i = 0; i < dax_dev->num_resources; i++) {
-               res = &dax_dev->res[i];
+               res = dax_dev->res[i];
                phys = pgoff * PAGE_SIZE + res->start;
                if (phys >= res->start && phys <= res->end)
                        break;
@@ -370,7 +421,7 @@ static phys_addr_t pgoff_to_phys(struct dax_dev *dax_dev, pgoff_t pgoff,
        }
 
        if (i < dax_dev->num_resources) {
-               res = &dax_dev->res[i];
+               res = dax_dev->res[i];
                if (phys + size - 1 <= res->end)
                        return phys;
        }
@@ -382,7 +433,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma,
                struct vm_fault *vmf)
 {
        unsigned long vaddr = (unsigned long) vmf->virtual_address;
-       struct device *dev = dax_dev->dev;
+       struct device *dev = &dax_dev->dev;
        struct dax_region *dax_region;
        int rc = VM_FAULT_SIGBUS;
        phys_addr_t phys;
@@ -422,7 +473,7 @@ static int dax_dev_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        struct file *filp = vma->vm_file;
        struct dax_dev *dax_dev = filp->private_data;
 
-       dev_dbg(dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__,
+       dev_dbg(&dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__,
                        current->comm, (vmf->flags & FAULT_FLAG_WRITE)
                        ? "write" : "read", vma->vm_start, vma->vm_end);
        rcu_read_lock();
@@ -437,7 +488,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
                unsigned int flags)
 {
        unsigned long pmd_addr = addr & PMD_MASK;
-       struct device *dev = dax_dev->dev;
+       struct device *dev = &dax_dev->dev;
        struct dax_region *dax_region;
        phys_addr_t phys;
        pgoff_t pgoff;
@@ -479,7 +530,7 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
        struct file *filp = vma->vm_file;
        struct dax_dev *dax_dev = filp->private_data;
 
-       dev_dbg(dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__,
+       dev_dbg(&dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__,
                        current->comm, (flags & FAULT_FLAG_WRITE)
                        ? "write" : "read", vma->vm_start, vma->vm_end);
 
@@ -490,81 +541,305 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
        return rc;
 }
 
-static void dax_dev_vm_open(struct vm_area_struct *vma)
-{
-       struct file *filp = vma->vm_file;
-       struct dax_dev *dax_dev = filp->private_data;
-
-       dev_dbg(dax_dev->dev, "%s\n", __func__);
-       kref_get(&dax_dev->kref);
-}
-
-static void dax_dev_vm_close(struct vm_area_struct *vma)
-{
-       struct file *filp = vma->vm_file;
-       struct dax_dev *dax_dev = filp->private_data;
-
-       dev_dbg(dax_dev->dev, "%s\n", __func__);
-       dax_dev_put(dax_dev);
-}
-
 static const struct vm_operations_struct dax_dev_vm_ops = {
        .fault = dax_dev_fault,
        .pmd_fault = dax_dev_pmd_fault,
-       .open = dax_dev_vm_open,
-       .close = dax_dev_vm_close,
 };
 
-static int dax_dev_mmap(struct file *filp, struct vm_area_struct *vma)
+static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
 {
        struct dax_dev *dax_dev = filp->private_data;
        int rc;
 
-       dev_dbg(dax_dev->dev, "%s\n", __func__);
+       dev_dbg(&dax_dev->dev, "%s\n", __func__);
 
        rc = check_vma(dax_dev, vma, __func__);
        if (rc)
                return rc;
 
-       kref_get(&dax_dev->kref);
        vma->vm_ops = &dax_dev_vm_ops;
        vma->vm_flags |= VM_MIXEDMAP | VM_HUGEPAGE;
        return 0;
+}
+
+/* return an unmapped area aligned to the dax region specified alignment */
+static unsigned long dax_get_unmapped_area(struct file *filp,
+               unsigned long addr, unsigned long len, unsigned long pgoff,
+               unsigned long flags)
+{
+       unsigned long off, off_end, off_align, len_align, addr_align, align;
+       struct dax_dev *dax_dev = filp ? filp->private_data : NULL;
+       struct dax_region *dax_region;
+
+       if (!dax_dev || addr)
+               goto out;
+
+       dax_region = dax_dev->region;
+       align = dax_region->align;
+       off = pgoff << PAGE_SHIFT;
+       off_end = off + len;
+       off_align = round_up(off, align);
+
+       if ((off_end <= off_align) || ((off_end - off_align) < align))
+               goto out;
+
+       len_align = len + align;
+       if ((off + len_align) < off)
+               goto out;
 
+       addr_align = current->mm->get_unmapped_area(filp, addr, len_align,
+                       pgoff, flags);
+       if (!IS_ERR_VALUE(addr_align)) {
+               addr_align += (off - addr_align) & (align - 1);
+               return addr_align;
+       }
+ out:
+       return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags);
+}
+
+static int dax_open(struct inode *inode, struct file *filp)
+{
+       struct dax_dev *dax_dev;
+
+       dax_dev = container_of(inode->i_cdev, struct dax_dev, cdev);
+       dev_dbg(&dax_dev->dev, "%s\n", __func__);
+       inode->i_mapping = dax_dev->inode->i_mapping;
+       inode->i_mapping->host = dax_dev->inode;
+       filp->f_mapping = inode->i_mapping;
+       filp->private_data = dax_dev;
+       inode->i_flags = S_DAX;
+
+       return 0;
+}
+
+static int dax_release(struct inode *inode, struct file *filp)
+{
+       struct dax_dev *dax_dev = filp->private_data;
+
+       dev_dbg(&dax_dev->dev, "%s\n", __func__);
+       return 0;
 }
 
 static const struct file_operations dax_fops = {
        .llseek = noop_llseek,
        .owner = THIS_MODULE,
-       .open = dax_dev_open,
-       .release = dax_dev_release,
-       .get_unmapped_area = dax_dev_get_unmapped_area,
-       .mmap = dax_dev_mmap,
+       .open = dax_open,
+       .release = dax_release,
+       .get_unmapped_area = dax_get_unmapped_area,
+       .mmap = dax_mmap,
 };
 
+static void dax_dev_release(struct device *dev)
+{
+       struct dax_dev *dax_dev = to_dax_dev(dev);
+       struct dax_region *dax_region = dax_dev->region;
+
+       ida_simple_remove(&dax_region->ida, dax_dev->id);
+       ida_simple_remove(&dax_minor_ida, MINOR(dev->devt));
+       dax_region_put(dax_region);
+       iput(dax_dev->inode);
+       kfree(dax_dev->res);
+       kfree(dax_dev);
+}
+
+static void unregister_dax_dev(void *dev)
+{
+       struct dax_dev *dax_dev = to_dax_dev(dev);
+       struct dax_region *dax_region = dax_dev->region;
+       struct cdev *cdev = &dax_dev->cdev;
+       int i;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       /*
+        * Note, rcu is not protecting the liveness of dax_dev, rcu is
+        * ensuring that any fault handlers that might have seen
+        * dax_dev->alive == true, have completed.  Any fault handlers
+        * that start after synchronize_rcu() has started will abort
+        * upon seeing dax_dev->alive == false.
+        */
+       dax_dev->alive = false;
+       synchronize_rcu();
+       unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);
+
+       mutex_lock(&dax_region->lock);
+       for (i = 0; i < dax_dev->num_resources; i++)
+               __release_region(&dax_region->res, dax_dev->res[i]->start,
+                               resource_size(dax_dev->res[i]));
+       if (dax_region->seed == dev)
+               dax_region->seed = NULL;
+       mutex_unlock(&dax_region->lock);
+       atomic_dec(&dax_region->child_count);
+
+       cdev_del(cdev);
+       device_unregister(dev);
+}
+
+struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
+               struct resource *res, int count)
+{
+       struct device *parent = dax_region->dev;
+       struct dax_dev *dax_dev;
+       int rc = 0, minor, i;
+       struct device *dev;
+       struct cdev *cdev;
+       dev_t dev_t;
+
+       dax_dev = kzalloc(sizeof(*dax_dev), GFP_KERNEL);
+       if (!dax_dev)
+               return ERR_PTR(-ENOMEM);
+
+       dax_dev->res = kzalloc(sizeof(res) * count, GFP_KERNEL);
+       if (!dax_dev->res)
+               goto err_res;
+
+       for (i = 0; i < count; i++) {
+               struct resource *dax_res;
+
+               if (!IS_ALIGNED(res[i].start, dax_region->align)
+                               || !IS_ALIGNED(resource_size(&res[i]),
+                                       dax_region->align)) {
+                       rc = -EINVAL;
+                       break;
+               }
+
+               mutex_lock(&dax_region->lock);
+               dax_res = __request_region(&dax_region->res, res[i].start,
+                               resource_size(&res[i]), NULL, 0);
+               mutex_unlock(&dax_region->lock);
+               if (!dax_res) {
+                       rc = -EBUSY;
+                       break;
+               }
+               dax_dev->res[i] = dax_res;
+       }
+
+       if (i < count)
+               goto err_request_region;
+
+       dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
+       if (dax_dev->id < 0) {
+               rc = dax_dev->id;
+               goto err_request_region;
+       }
+
+       minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL);
+       if (minor < 0) {
+               rc = minor;
+               goto err_minor;
+       }
+
+       dev_t = MKDEV(MAJOR(dax_devt), minor);
+       dev = &dax_dev->dev;
+       dax_dev->inode = dax_inode_get(&dax_dev->cdev, dev_t);
+       if (!dax_dev->inode) {
+               rc = -ENOMEM;
+               goto err_inode;
+       }
+
+       /* device_initialize() so cdev can reference kobj parent */
+       device_initialize(dev);
+
+       cdev = &dax_dev->cdev;
+       cdev_init(cdev, &dax_fops);
+       cdev->owner = parent->driver->owner;
+       cdev->kobj.parent = &dev->kobj;
+       rc = cdev_add(&dax_dev->cdev, dev_t, 1);
+       if (rc)
+               goto err_cdev;
+
+       /* from here on we're committed to teardown via dax_dev_release() */
+       dax_dev->num_resources = count;
+       dax_dev->alive = true;
+       dax_dev->region = dax_region;
+       kref_get(&dax_region->kref);
+
+       dev->devt = dev_t;
+       dev->class = dax_class;
+       dev->parent = parent;
+       dev->groups = dax_attribute_groups;
+       dev->release = dax_dev_release;
+       dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
+       /* update resource names now that the owner device is named */
+       for (i = 0; i < dax_dev->num_resources; i++)
+               dax_dev->res[i]->name = dev_name(dev);
+
+       rc = device_add(dev);
+       if (rc) {
+               put_device(dev);
+               return ERR_PTR(rc);
+       }
+
+       rc = devm_add_action_or_reset(dax_region->dev, unregister_dax_dev, dev);
+       if (rc)
+               return ERR_PTR(rc);
+
+       if (atomic_inc_return(&dax_region->child_count) == 1) {
+               struct dax_dev *seed;
+
+               seed = devm_create_dax_dev(dax_region, NULL, 0);
+               if (IS_ERR(seed))
+                       dev_warn(parent, "failed to create region seed\n");
+               else
+                       dax_region->seed = &seed->dev;
+       }
+
+       return dax_dev;
+
+ err_cdev:
+       iput(dax_dev->inode);
+ err_inode:
+       ida_simple_remove(&dax_minor_ida, minor);
+ err_minor:
+       ida_simple_remove(&dax_region->ida, dax_dev->id);
+ err_request_region:
+       mutex_lock(&dax_region->lock);
+       for (i--; i >= 0; i--)
+               __release_region(&dax_region->res, dax_dev->res[i]->start,
+                               resource_size(dax_dev->res[i]));
+       mutex_unlock(&dax_region->lock);
+       kfree(dax_dev->res);
+ err_res:
+       kfree(dax_dev);
+
+       return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(devm_create_dax_dev);
+
 static int __init dax_init(void)
 {
        int rc;
 
-       rc = register_chrdev(0, "dax", &dax_fops);
-       if (rc < 0)
+       rc = dax_inode_init();
+       if (rc)
                return rc;
-       dax_major = rc;
+
+       nr_dax = max(nr_dax, 256);
+       rc = alloc_chrdev_region(&dax_devt, 0, nr_dax, "dax");
+       if (rc)
+               goto err_chrdev;
 
        dax_class = class_create(THIS_MODULE, "dax");
        if (IS_ERR(dax_class)) {
-               unregister_chrdev(dax_major, "dax");
-               return PTR_ERR(dax_class);
+               rc = PTR_ERR(dax_class);
+               goto err_class;
        }
 
        return 0;
+
+ err_class:
+       unregister_chrdev_region(dax_devt, nr_dax);
+ err_chrdev:
+       dax_inode_exit();
+       return rc;
 }
 
 static void __exit dax_exit(void)
 {
        class_destroy(dax_class);
-       unregister_chrdev(dax_major, "dax");
+       unregister_chrdev_region(dax_devt, nr_dax);
        ida_destroy(&dax_minor_ida);
+       dax_inode_exit();
 }
 
 MODULE_AUTHOR("Intel Corporation");
index d8b8f1f25054d2f819dc2d47370e47ec592e8ade..ddd829ab58c05884ffea85bfab8e198ce6303b72 100644 (file)
 #ifndef __DAX_H__
 #define __DAX_H__
 struct device;
+struct dax_dev;
 struct resource;
 struct dax_region;
 void dax_region_put(struct dax_region *dax_region);
 struct dax_region *alloc_dax_region(struct device *parent,
                int region_id, struct resource *res, unsigned int align,
                void *addr, unsigned long flags);
-int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res,
-               int count);
+struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
+               struct resource *res, int count);
 #endif /* __DAX_H__ */
index 1f01e98c83c7ddcccecd50e11671e3b519b2c136..9630d8837ba94ed3badc9a3755b53c08123b9a51 100644 (file)
@@ -24,7 +24,7 @@ struct dax_pmem {
        struct completion cmp;
 };
 
-struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
+static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
 {
        return container_of(ref, struct dax_pmem, ref);
 }
@@ -61,6 +61,7 @@ static int dax_pmem_probe(struct device *dev)
        int rc;
        void *addr;
        struct resource res;
+       struct dax_dev *dax_dev;
        struct nd_pfn_sb *pfn_sb;
        struct dax_pmem *dax_pmem;
        struct nd_region *nd_region;
@@ -126,12 +127,12 @@ static int dax_pmem_probe(struct device *dev)
                return -ENOMEM;
 
        /* TODO: support for subdividing a dax region... */
-       rc = devm_create_dax_dev(dax_region, &res, 1);
+       dax_dev = devm_create_dax_dev(dax_region, &res, 1);
 
        /* child dax_dev instances now own the lifetime of the dax_region */
        dax_region_put(dax_region);
 
-       return rc;
+       return PTR_ERR_OR_ZERO(dax_dev);
 }
 
 static struct nd_device_driver dax_pmem_driver = {
index 71d12bb67339148bac8edc2e70c907aff1852f5a..619834e144d1e65e2314cde9356c86cff44d5053 100644 (file)
@@ -26,6 +26,14 @@ static int nvdimm_probe(struct device *dev)
        struct nvdimm_drvdata *ndd;
        int rc;
 
+       rc = nvdimm_check_config_data(dev);
+       if (rc) {
+               /* not required for non-aliased nvdimm, ex. NVDIMM-N */
+               if (rc == -ENOTTY)
+                       rc = 0;
+               return rc;
+       }
+
        ndd = kzalloc(sizeof(*ndd), GFP_KERNEL);
        if (!ndd)
                return -ENOMEM;
@@ -72,6 +80,9 @@ static int nvdimm_remove(struct device *dev)
 {
        struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
 
+       if (!ndd)
+               return 0;
+
        nvdimm_bus_lock(dev);
        dev_set_drvdata(dev, NULL);
        nvdimm_bus_unlock(dev);
index d9bba5edd8dcf0646cad13a0160534648c52ce8d..cf36470e94c0d805fea2fedccb52528325e2bac8 100644 (file)
@@ -28,28 +28,30 @@ static DEFINE_IDA(dimm_ida);
  * Retrieve bus and dimm handle and return if this bus supports
  * get_config_data commands
  */
-static int __validate_dimm(struct nvdimm_drvdata *ndd)
+int nvdimm_check_config_data(struct device *dev)
 {
-       struct nvdimm *nvdimm;
-
-       if (!ndd)
-               return -EINVAL;
-
-       nvdimm = to_nvdimm(ndd->dev);
+       struct nvdimm *nvdimm = to_nvdimm(dev);
 
-       if (!nvdimm->cmd_mask)
-               return -ENXIO;
-       if (!test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask))
-               return -ENXIO;
+       if (!nvdimm->cmd_mask ||
+           !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
+               if (nvdimm->flags & NDD_ALIASING)
+                       return -ENXIO;
+               else
+                       return -ENOTTY;
+       }
 
        return 0;
 }
 
 static int validate_dimm(struct nvdimm_drvdata *ndd)
 {
-       int rc = __validate_dimm(ndd);
+       int rc;
+
+       if (!ndd)
+               return -EINVAL;
 
-       if (rc && ndd)
+       rc = nvdimm_check_config_data(ndd->dev);
+       if (rc)
                dev_dbg(ndd->dev, "%pf: %s error: %d\n",
                                __builtin_return_address(0), __func__, rc);
        return rc;
@@ -263,6 +265,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm)
 }
 EXPORT_SYMBOL_GPL(nvdimm_name);
 
+struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
+{
+       return &nvdimm->dev.kobj;
+}
+EXPORT_SYMBOL_GPL(nvdimm_kobj);
+
 unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
 {
        return nvdimm->cmd_mask;
index c5e3196c45b02cc5f9070ff8cd9289e46c926ee1..6b0449bd7720b7c0aebc11313f5574b5360de239 100644 (file)
@@ -294,7 +294,7 @@ static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk)
                if (strcmp(res->name, label_id.id) != 0)
                        continue;
                /*
-                * Resources with unacknoweldged adjustments indicate a
+                * Resources with unacknowledged adjustments indicate a
                 * failure to update labels
                 */
                if (res->flags & DPA_RESOURCE_ADJUSTED)
index 8024a0ef86d3af9f0ba5ef169260e2e342023d8e..38d6f039234e7aae0e6a9e9923d23e979137448f 100644 (file)
@@ -191,6 +191,7 @@ void nvdimm_exit(void);
 void nd_region_exit(void);
 struct nvdimm;
 struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping);
+int nvdimm_check_config_data(struct device *dev);
 int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd);
 int nvdimm_init_config_data(struct nvdimm_drvdata *ndd);
 int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
index 6edd825231c5d3d323d2e58cd2ac97189ab61470..44a240c4bb658149c2d2b5b527af35fbf1a95d49 100644 (file)
@@ -406,6 +406,7 @@ void cd_forget(struct inode *inode)
        spin_lock(&cdev_lock);
        list_del_init(&inode->i_devices);
        inode->i_cdev = NULL;
+       inode->i_mapping = &inode->i_data;
        spin_unlock(&cdev_lock);
 }
 
index b519e137b9b7d98ab44aa1409510ad35e841c10f..ad18d0531b6e89faf00a6d92280cce8bafcdafcb 100644 (file)
@@ -139,6 +139,7 @@ struct nd_blk_region *to_nd_blk_region(struct device *dev);
 struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
 struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus);
 const char *nvdimm_name(struct nvdimm *nvdimm);
+struct kobject *nvdimm_kobj(struct nvdimm *nvdimm);
 unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
 void *nvdimm_provider_data(struct nvdimm *nvdimm);
 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
index e398beac67b89eb5afc863fc2f38fc9623e24211..9bd559472c9280a6317e336f3a62471fd0aafa49 100644 (file)
@@ -65,6 +65,7 @@
 #define V9FS_MAGIC             0x01021997
 
 #define BDEVFS_MAGIC            0x62646576
+#define DAXFS_MAGIC             0x64646178
 #define BINFMTFS_MAGIC          0x42494e4d
 #define DEVPTS_SUPER_MAGIC     0x1cd1
 #define FUTEXFS_SUPER_MAGIC    0xBAD1DEA
index ba5a8c79652a469f048d1507bea9e489ed698bef..ede5c6a62164ae182f4495724bf4a518e5b1b93b 100644 (file)
@@ -21,14 +21,16 @@ struct nd_cmd_smart {
 } __packed;
 
 #define ND_SMART_HEALTH_VALID  (1 << 0)
-#define ND_SMART_TEMP_VALID    (1 << 1)
-#define ND_SMART_SPARES_VALID  (1 << 2)
-#define ND_SMART_ALARM_VALID   (1 << 3)
-#define ND_SMART_USED_VALID    (1 << 4)
-#define ND_SMART_SHUTDOWN_VALID        (1 << 5)
-#define ND_SMART_VENDOR_VALID  (1 << 6)
-#define ND_SMART_TEMP_TRIP     (1 << 0)
-#define ND_SMART_SPARE_TRIP    (1 << 1)
+#define ND_SMART_SPARES_VALID  (1 << 1)
+#define ND_SMART_USED_VALID    (1 << 2)
+#define ND_SMART_TEMP_VALID    (1 << 3)
+#define ND_SMART_CTEMP_VALID   (1 << 4)
+#define ND_SMART_ALARM_VALID   (1 << 9)
+#define ND_SMART_SHUTDOWN_VALID        (1 << 10)
+#define ND_SMART_VENDOR_VALID  (1 << 11)
+#define ND_SMART_SPARE_TRIP    (1 << 0)
+#define ND_SMART_TEMP_TRIP     (1 << 1)
+#define ND_SMART_CTEMP_TRIP    (1 << 2)
 #define ND_SMART_NON_CRITICAL_HEALTH   (1 << 0)
 #define ND_SMART_CRITICAL_HEALTH       (1 << 1)
 #define ND_SMART_FATAL_HEALTH          (1 << 2)
@@ -37,14 +39,15 @@ struct nd_smart_payload {
        __u32 flags;
        __u8 reserved0[4];
        __u8 health;
-       __u16 temperature;
        __u8 spares;
-       __u8 alarm_flags;
        __u8 life_used;
+       __u8 alarm_flags;
+       __u16 temperature;
+       __u16 ctrl_temperature;
+       __u8 reserved1[15];
        __u8 shutdown_state;
-       __u8 reserved1;
        __u32 vendor_size;
-       __u8 vendor_data[108];
+       __u8 vendor_data[92];
 } __packed;
 
 struct nd_cmd_smart_threshold {
@@ -53,7 +56,8 @@ struct nd_cmd_smart_threshold {
 } __packed;
 
 struct nd_smart_threshold_payload {
-       __u16 alarm_control;
+       __u8 alarm_control;
+       __u8 reserved0;
        __u16 temperature;
        __u8 spares;
        __u8 reserved[3];
index ad6dd05430192ffdba8d163f9855942fa75b4099..582db95127ed41cd5491562613a70ca7623bd190 100644 (file)
@@ -13,6 +13,7 @@ ldflags-y += --wrap=__release_region
 ldflags-y += --wrap=devm_memremap_pages
 ldflags-y += --wrap=insert_resource
 ldflags-y += --wrap=remove_resource
+ldflags-y += --wrap=acpi_evaluate_object
 
 DRIVERS := ../../../drivers
 NVDIMM_SRC := $(DRIVERS)/nvdimm
index c29f8dca9e67c1f95da2861078ffee61b258ed69..dae5b9b6d186a5e227c475afafc5b2069d2121fd 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/pfn_t.h>
+#include <linux/acpi.h>
 #include <linux/io.h>
 #include <linux/mm.h>
 #include "nfit_test.h"
@@ -276,4 +277,20 @@ void __wrap___devm_release_region(struct device *dev, struct resource *parent,
 }
 EXPORT_SYMBOL(__wrap___devm_release_region);
 
+acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path,
+               struct acpi_object_list *p, struct acpi_buffer *buf)
+{
+       struct nfit_test_resource *nfit_res = get_nfit_res((long) handle);
+       union acpi_object **obj;
+
+       if (!nfit_res || strcmp(path, "_FIT") || !buf)
+               return acpi_evaluate_object(handle, path, p, buf);
+
+       obj = nfit_res->buf;
+       buf->length = sizeof(union acpi_object);
+       buf->pointer = *obj;
+       return AE_OK;
+}
+EXPORT_SYMBOL(__wrap_acpi_evaluate_object);
+
 MODULE_LICENSE("GPL v2");
index dd48f421844c7902773526d8c0845109ab9c5e55..99ea68674f0a99d5baae862dae802f4452f0c7df 100644 (file)
@@ -154,11 +154,14 @@ struct nfit_test {
        int (*alloc)(struct nfit_test *t);
        void (*setup)(struct nfit_test *t);
        int setup_hotplug;
+       union acpi_object **_fit;
+       dma_addr_t _fit_dma;
        struct ars_state {
                struct nd_cmd_ars_status *ars_status;
                unsigned long deadline;
                spinlock_t lock;
        } ars_state;
+       struct device *dimm_dev[NUM_DCR];
 };
 
 static struct nfit_test *to_nfit_test(struct device *dev)
@@ -428,6 +431,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
                        break;
                case ND_CMD_SMART_THRESHOLD:
                        rc = nfit_test_cmd_smart_threshold(buf, buf_len);
+                       device_lock(&t->pdev.dev);
+                       __acpi_nvdimm_notify(t->dimm_dev[i], 0x81);
+                       device_unlock(&t->pdev.dev);
                        break;
                default:
                        return -ENOTTY;
@@ -564,6 +570,18 @@ static int ars_state_init(struct device *dev, struct ars_state *ars_state)
        return 0;
 }
 
+static void put_dimms(void *data)
+{
+       struct device **dimm_dev = data;
+       int i;
+
+       for (i = 0; i < NUM_DCR; i++)
+               if (dimm_dev[i])
+                       device_unregister(dimm_dev[i]);
+}
+
+static struct class *nfit_test_dimm;
+
 static int nfit_test0_alloc(struct nfit_test *t)
 {
        size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA
@@ -615,6 +633,19 @@ static int nfit_test0_alloc(struct nfit_test *t)
                        return -ENOMEM;
        }
 
+       t->_fit = test_alloc(t, sizeof(union acpi_object **), &t->_fit_dma);
+       if (!t->_fit)
+               return -ENOMEM;
+
+       if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t->dimm_dev))
+               return -ENOMEM;
+       for (i = 0; i < NUM_DCR; i++) {
+               t->dimm_dev[i] = device_create(nfit_test_dimm, &t->pdev.dev, 0,
+                               NULL, "test_dimm%d", i);
+               if (!t->dimm_dev[i])
+                       return -ENOMEM;
+       }
+
        return ars_state_init(&t->pdev.dev, &t->ars_state);
 }
 
@@ -1408,6 +1439,8 @@ static int nfit_test_probe(struct platform_device *pdev)
        struct acpi_nfit_desc *acpi_desc;
        struct device *dev = &pdev->dev;
        struct nfit_test *nfit_test;
+       struct nfit_mem *nfit_mem;
+       union acpi_object *obj;
        int rc;
 
        nfit_test = to_nfit_test(&pdev->dev);
@@ -1475,14 +1508,30 @@ static int nfit_test_probe(struct platform_device *pdev)
        if (nfit_test->setup != nfit_test0_setup)
                return 0;
 
-       flush_work(&acpi_desc->work);
        nfit_test->setup_hotplug = 1;
        nfit_test->setup(nfit_test);
 
-       rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf,
-                       nfit_test->nfit_size);
-       if (rc)
-               return rc;
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return -ENOMEM;
+       obj->type = ACPI_TYPE_BUFFER;
+       obj->buffer.length = nfit_test->nfit_size;
+       obj->buffer.pointer = nfit_test->nfit_buf;
+       *(nfit_test->_fit) = obj;
+       __acpi_nfit_notify(&pdev->dev, nfit_test, 0x80);
+
+       /* associate dimm devices with nfit_mem data for notification testing */
+       mutex_lock(&acpi_desc->init_mutex);
+       list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+               u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle;
+               int i;
+
+               for (i = 0; i < NUM_DCR; i++)
+                       if (nfit_handle == handle[i])
+                               dev_set_drvdata(nfit_test->dimm_dev[i],
+                                               nfit_mem);
+       }
+       mutex_unlock(&acpi_desc->init_mutex);
 
        return 0;
 }
@@ -1517,6 +1566,10 @@ static __init int nfit_test_init(void)
 {
        int rc, i;
 
+       nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm");
+       if (IS_ERR(nfit_test_dimm))
+               return PTR_ERR(nfit_test_dimm);
+
        nfit_test_setup(nfit_test_lookup);
 
        for (i = 0; i < NUM_NFITS; i++) {
@@ -1583,6 +1636,7 @@ static __exit void nfit_test_exit(void)
        for (i = 0; i < NUM_NFITS; i++)
                platform_device_unregister(&instances[i]->pdev);
        nfit_test_teardown();
+       class_destroy(nfit_test_dimm);
 }
 
 module_init(nfit_test_init);
This page took 0.047993 seconds and 5 git commands to generate.