]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/dax/dax.c
UBUNTU: SAUCE: s390/mm: fix local TLB flushing vs. detach of an mm address space
[mirror_ubuntu-zesty-kernel.git] / drivers / dax / dax.c
index ed758b74ddf0b74fe3fdbc84f8dde771aead4aca..82e42867a21f137d70ef528202ebcc69be5b5ef5 100644 (file)
@@ -24,6 +24,7 @@
 #include "dax.h"
 
 static dev_t dax_devt;
+DEFINE_STATIC_SRCU(dax_srcu);
 static struct class *dax_class;
 static DEFINE_IDA(dax_minor_ida);
 static int nr_dax = CONFIG_NR_DEV_DAX;
@@ -59,7 +60,7 @@ struct dax_region {
  * @region - parent region
  * @dev - device backing the character device
  * @cdev - core chardev data
- * @alive - !alive + rcu grace period == no new mappings can be established
+ * @alive - !alive + srcu grace period == no new mappings can be established
  * @id - child id in the region
  * @num_resources - number of physical address extents in this device
  * @res - array of physical address ranges
@@ -75,36 +76,27 @@ struct dax_dev {
        struct resource res[0];
 };
 
+/*
+ * Rely on the fact that drvdata is set before the attributes are
+ * registered, and that the attributes are unregistered before drvdata
+ * is cleared to assume that drvdata is always valid.
+ */
 static ssize_t id_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct dax_region *dax_region;
-       ssize_t rc = -ENXIO;
-
-       device_lock(dev);
-       dax_region = dev_get_drvdata(dev);
-       if (dax_region)
-               rc = sprintf(buf, "%d\n", dax_region->id);
-       device_unlock(dev);
+       struct dax_region *dax_region = dev_get_drvdata(dev);
 
-       return rc;
+       return sprintf(buf, "%d\n", dax_region->id);
 }
 static DEVICE_ATTR_RO(id);
 
 static ssize_t region_size_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct dax_region *dax_region;
-       ssize_t rc = -ENXIO;
+       struct dax_region *dax_region = dev_get_drvdata(dev);
 
-       device_lock(dev);
-       dax_region = dev_get_drvdata(dev);
-       if (dax_region)
-               rc = sprintf(buf, "%llu\n", (unsigned long long)
-                               resource_size(&dax_region->res));
-       device_unlock(dev);
-
-       return rc;
+       return sprintf(buf, "%llu\n", (unsigned long long)
+                       resource_size(&dax_region->res));
 }
 static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
                region_size_show, NULL);
@@ -112,16 +104,9 @@ static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
 static ssize_t align_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct dax_region *dax_region;
-       ssize_t rc = -ENXIO;
-
-       device_lock(dev);
-       dax_region = dev_get_drvdata(dev);
-       if (dax_region)
-               rc = sprintf(buf, "%u\n", dax_region->align);
-       device_unlock(dev);
+       struct dax_region *dax_region = dev_get_drvdata(dev);
 
-       return rc;
+       return sprintf(buf, "%u\n", dax_region->align);
 }
 static DEVICE_ATTR_RO(align);
 
@@ -427,6 +412,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma,
        int rc = VM_FAULT_SIGBUS;
        phys_addr_t phys;
        pfn_t pfn;
+       unsigned int fault_size = PAGE_SIZE;
 
        if (check_vma(dax_dev, vma, __func__))
                return VM_FAULT_SIGBUS;
@@ -437,6 +423,9 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma,
                return VM_FAULT_SIGBUS;
        }
 
+       if (fault_size != dax_region->align)
+               return VM_FAULT_SIGBUS;
+
        phys = pgoff_to_phys(dax_dev, vmf->pgoff, PAGE_SIZE);
        if (phys == -1) {
                dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__,
@@ -482,6 +471,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
        phys_addr_t phys;
        pgoff_t pgoff;
        pfn_t pfn;
+       unsigned int fault_size = PMD_SIZE;
 
        if (check_vma(dax_dev, vma, __func__))
                return VM_FAULT_SIGBUS;
@@ -498,6 +488,16 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
                return VM_FAULT_SIGBUS;
        }
 
+       if (fault_size < dax_region->align)
+               return VM_FAULT_SIGBUS;
+       else if (fault_size > dax_region->align)
+               return VM_FAULT_FALLBACK;
+
+       /* if we are outside of the VMA */
+       if (pmd_addr < vma->vm_start ||
+                       (pmd_addr + PMD_SIZE) > vma->vm_end)
+               return VM_FAULT_SIGBUS;
+
        pgoff = linear_page_index(vma, pmd_addr);
        phys = pgoff_to_phys(dax_dev, pgoff, PMD_SIZE);
        if (phys == -1) {
@@ -515,7 +515,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
 static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
                pmd_t *pmd, unsigned int flags)
 {
-       int rc;
+       int rc, id;
        struct file *filp = vma->vm_file;
        struct dax_dev *dax_dev = filp->private_data;
 
@@ -523,9 +523,9 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
                        current->comm, (flags & FAULT_FLAG_WRITE)
                        ? "write" : "read", vma->vm_start, vma->vm_end);
 
-       rcu_read_lock();
+       id = srcu_read_lock(&dax_srcu);
        rc = __dax_dev_pmd_fault(dax_dev, vma, addr, pmd, flags);
-       rcu_read_unlock();
+       srcu_read_unlock(&dax_srcu, id);
 
        return rc;
 }
@@ -630,24 +630,30 @@ static void dax_dev_release(struct device *dev)
        kfree(dax_dev);
 }
 
-static void unregister_dax_dev(void *dev)
+static void kill_dax_dev(struct dax_dev *dax_dev)
 {
-       struct dax_dev *dax_dev = to_dax_dev(dev);
        struct cdev *cdev = &dax_dev->cdev;
 
-       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
+        * that start after synchronize_srcu() has started will abort
         * upon seeing dax_dev->alive == false.
         */
        dax_dev->alive = false;
-       synchronize_rcu();
+       synchronize_srcu(&dax_srcu);
        unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);
        cdev_del(cdev);
+}
+
+static void unregister_dax_dev(void *dev)
+{
+       struct dax_dev *dax_dev = to_dax_dev(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       kill_dax_dev(dax_dev);
        device_unregister(dev);
 }
 
@@ -724,6 +730,7 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
        dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
        rc = device_add(dev);
        if (rc) {
+               kill_dax_dev(dax_dev);
                put_device(dev);
                return ERR_PTR(rc);
        }