Merge tag 'drm-amdkfd-fixes-2015-07-09' of git://people.freedesktop.org/~gabbayo...
[firefly-linux-kernel-4.4.55.git] / drivers / nvdimm / namespace_devs.c
index 50b502b1908eecd7605d092230a88c016fc65f59..fef0dd80d4adb18c10a62a89ae0a53a78c9ea091 100644 (file)
@@ -76,6 +76,30 @@ static bool is_namespace_io(struct device *dev)
        return dev ? dev->type == &namespace_io_device_type : false;
 }
 
+const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns,
+               char *name)
+{
+       struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
+       const char *suffix = "";
+
+       if (ndns->claim && is_nd_btt(ndns->claim))
+               suffix = "s";
+
+       if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev))
+               sprintf(name, "pmem%d%s", nd_region->id, suffix);
+       else if (is_namespace_blk(&ndns->dev)) {
+               struct nd_namespace_blk *nsblk;
+
+               nsblk = to_nd_namespace_blk(&ndns->dev);
+               sprintf(name, "ndblk%d.%d%s", nd_region->id, nsblk->id, suffix);
+       } else {
+               return NULL;
+       }
+
+       return name;
+}
+EXPORT_SYMBOL(nvdimm_namespace_disk_name);
+
 static ssize_t nstype_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -102,7 +126,7 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf,
        } else
                return -ENXIO;
 
-       if (dev->driver)
+       if (dev->driver || to_ndns(dev)->claim)
                return -EBUSY;
 
        input = kmemdup(buf, len + 1, GFP_KERNEL);
@@ -133,7 +157,7 @@ out:
 
 static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk)
 {
-       struct nd_region *nd_region = to_nd_region(nsblk->dev.parent);
+       struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent);
        struct nd_mapping *nd_mapping = &nd_region->mapping[0];
        struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
        struct nd_label_id label_id;
@@ -149,12 +173,71 @@ static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk)
        return size;
 }
 
+static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk)
+{
+       struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent);
+       struct nd_mapping *nd_mapping = &nd_region->mapping[0];
+       struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+       struct nd_label_id label_id;
+       struct resource *res;
+       int count, i;
+
+       if (!nsblk->uuid || !nsblk->lbasize || !ndd)
+               return false;
+
+       count = 0;
+       nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL);
+       for_each_dpa_resource(ndd, res) {
+               if (strcmp(res->name, label_id.id) != 0)
+                       continue;
+               /*
+                * Resources with unacknoweldged adjustments indicate a
+                * failure to update labels
+                */
+               if (res->flags & DPA_RESOURCE_ADJUSTED)
+                       return false;
+               count++;
+       }
+
+       /* These values match after a successful label update */
+       if (count != nsblk->num_resources)
+               return false;
+
+       for (i = 0; i < nsblk->num_resources; i++) {
+               struct resource *found = NULL;
+
+               for_each_dpa_resource(ndd, res)
+                       if (res == nsblk->res[i]) {
+                               found = res;
+                               break;
+                       }
+               /* stale resource */
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
+resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk)
+{
+       resource_size_t size;
+
+       nvdimm_bus_lock(&nsblk->common.dev);
+       size = __nd_namespace_blk_validate(nsblk);
+       nvdimm_bus_unlock(&nsblk->common.dev);
+
+       return size;
+}
+EXPORT_SYMBOL(nd_namespace_blk_validate);
+
+
 static int nd_namespace_label_update(struct nd_region *nd_region,
                struct device *dev)
 {
-       dev_WARN_ONCE(dev, dev->driver,
+       dev_WARN_ONCE(dev, dev->driver || to_ndns(dev)->claim,
                        "namespace must be idle during label update\n");
-       if (dev->driver)
+       if (dev->driver || to_ndns(dev)->claim)
                return 0;
 
        /*
@@ -666,7 +749,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
        u8 *uuid = NULL;
        int rc, i;
 
-       if (dev->driver)
+       if (dev->driver || to_ndns(dev)->claim)
                return -EBUSY;
 
        if (is_namespace_pmem(dev)) {
@@ -733,12 +816,16 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
                nd_namespace_pmem_set_size(nd_region, nspm,
                                val * nd_region->ndr_mappings);
        } else if (is_namespace_blk(dev)) {
+               struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
+
                /*
                 * Try to delete the namespace if we deleted all of its
-                * allocation and this is not the seed device for the
-                * region.
+                * allocation, this is not the seed device for the
+                * region, and it is not actively claimed by a btt
+                * instance.
                 */
-               if (val == 0 && nd_region->ns_seed != dev)
+               if (val == 0 && nd_region->ns_seed != dev
+                               && !nsblk->common.claim)
                        nd_device_unregister(dev, ND_ASYNC);
        }
 
@@ -789,26 +876,42 @@ static ssize_t size_store(struct device *dev,
        return rc < 0 ? rc : len;
 }
 
-static ssize_t size_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
+resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns)
 {
-       unsigned long long size = 0;
+       struct device *dev = &ndns->dev;
 
-       nvdimm_bus_lock(dev);
        if (is_namespace_pmem(dev)) {
                struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
 
-               size = resource_size(&nspm->nsio.res);
+               return resource_size(&nspm->nsio.res);
        } else if (is_namespace_blk(dev)) {
-               size = nd_namespace_blk_size(to_nd_namespace_blk(dev));
+               return nd_namespace_blk_size(to_nd_namespace_blk(dev));
        } else if (is_namespace_io(dev)) {
                struct nd_namespace_io *nsio = to_nd_namespace_io(dev);
 
-               size = resource_size(&nsio->res);
-       }
-       nvdimm_bus_unlock(dev);
+               return resource_size(&nsio->res);
+       } else
+               WARN_ONCE(1, "unknown namespace type\n");
+       return 0;
+}
 
-       return sprintf(buf, "%llu\n", size);
+resource_size_t nvdimm_namespace_capacity(struct nd_namespace_common *ndns)
+{
+       resource_size_t size;
+
+       nvdimm_bus_lock(&ndns->dev);
+       size = __nvdimm_namespace_capacity(ndns);
+       nvdimm_bus_unlock(&ndns->dev);
+
+       return size;
+}
+EXPORT_SYMBOL(nvdimm_namespace_capacity);
+
+static ssize_t size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%llu\n", (unsigned long long)
+                       nvdimm_namespace_capacity(to_ndns(dev)));
 }
 static DEVICE_ATTR(size, S_IRUGO, size_show, size_store);
 
@@ -897,8 +1000,8 @@ static ssize_t uuid_store(struct device *dev,
 {
        struct nd_region *nd_region = to_nd_region(dev->parent);
        u8 *uuid = NULL;
+       ssize_t rc = 0;
        u8 **ns_uuid;
-       ssize_t rc;
 
        if (is_namespace_pmem(dev)) {
                struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
@@ -914,7 +1017,10 @@ static ssize_t uuid_store(struct device *dev,
        device_lock(dev);
        nvdimm_bus_lock(dev);
        wait_nvdimm_bus_probe_idle(dev);
-       rc = nd_uuid_store(dev, &uuid, buf, len);
+       if (to_ndns(dev)->claim)
+               rc = -EBUSY;
+       if (rc >= 0)
+               rc = nd_uuid_store(dev, &uuid, buf, len);
        if (rc >= 0)
                rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid);
        if (rc >= 0)
@@ -953,7 +1059,8 @@ static ssize_t resource_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(resource);
 
-static const unsigned long ns_lbasize_supported[] = { 512, 0 };
+static const unsigned long ns_lbasize_supported[] = { 512, 520, 528,
+       4096, 4104, 4160, 4224, 0 };
 
 static ssize_t sector_size_show(struct device *dev,
                struct device_attribute *attr, char *buf)
@@ -971,15 +1078,18 @@ static ssize_t sector_size_store(struct device *dev,
 {
        struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
        struct nd_region *nd_region = to_nd_region(dev->parent);
-       ssize_t rc;
+       ssize_t rc = 0;
 
        if (!is_namespace_blk(dev))
                return -ENXIO;
 
        device_lock(dev);
        nvdimm_bus_lock(dev);
-       rc = nd_sector_size_store(dev, buf, &nsblk->lbasize,
-                       ns_lbasize_supported);
+       if (to_ndns(dev)->claim)
+               rc = -EBUSY;
+       if (rc >= 0)
+               rc = nd_sector_size_store(dev, buf, &nsblk->lbasize,
+                               ns_lbasize_supported);
        if (rc >= 0)
                rc = nd_namespace_label_update(nd_region, dev);
        dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__,
@@ -1034,12 +1144,48 @@ static ssize_t dpa_extents_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(dpa_extents);
 
+static ssize_t holder_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nd_namespace_common *ndns = to_ndns(dev);
+       ssize_t rc;
+
+       device_lock(dev);
+       rc = sprintf(buf, "%s\n", ndns->claim ? dev_name(ndns->claim) : "");
+       device_unlock(dev);
+
+       return rc;
+}
+static DEVICE_ATTR_RO(holder);
+
+static ssize_t force_raw_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       bool force_raw;
+       int rc = strtobool(buf, &force_raw);
+
+       if (rc)
+               return rc;
+
+       to_ndns(dev)->force_raw = force_raw;
+       return len;
+}
+
+static ssize_t force_raw_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", to_ndns(dev)->force_raw);
+}
+static DEVICE_ATTR_RW(force_raw);
+
 static struct attribute *nd_namespace_attributes[] = {
        &dev_attr_nstype.attr,
        &dev_attr_size.attr,
        &dev_attr_uuid.attr,
+       &dev_attr_holder.attr,
        &dev_attr_resource.attr,
        &dev_attr_alt_name.attr,
+       &dev_attr_force_raw.attr,
        &dev_attr_sector_size.attr,
        &dev_attr_dpa_extents.attr,
        NULL,
@@ -1066,7 +1212,9 @@ static umode_t namespace_visible(struct kobject *kobj,
                return a->mode;
        }
 
-       if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr)
+       if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr
+                       || a == &dev_attr_holder.attr
+                       || a == &dev_attr_force_raw.attr)
                return a->mode;
 
        return 0;
@@ -1080,9 +1228,74 @@ static struct attribute_group nd_namespace_attribute_group = {
 static const struct attribute_group *nd_namespace_attribute_groups[] = {
        &nd_device_attribute_group,
        &nd_namespace_attribute_group,
+       &nd_numa_attribute_group,
        NULL,
 };
 
+struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
+{
+       struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL;
+       struct nd_namespace_common *ndns;
+       resource_size_t size;
+
+       if (nd_btt) {
+               ndns = nd_btt->ndns;
+               if (!ndns)
+                       return ERR_PTR(-ENODEV);
+
+               /*
+                * Flush any in-progess probes / removals in the driver
+                * for the raw personality of this namespace.
+                */
+               device_lock(&ndns->dev);
+               device_unlock(&ndns->dev);
+               if (ndns->dev.driver) {
+                       dev_dbg(&ndns->dev, "is active, can't bind %s\n",
+                                       dev_name(&nd_btt->dev));
+                       return ERR_PTR(-EBUSY);
+               }
+               if (dev_WARN_ONCE(&ndns->dev, ndns->claim != &nd_btt->dev,
+                                       "host (%s) vs claim (%s) mismatch\n",
+                                       dev_name(&nd_btt->dev),
+                                       dev_name(ndns->claim)))
+                       return ERR_PTR(-ENXIO);
+       } else {
+               ndns = to_ndns(dev);
+               if (ndns->claim) {
+                       dev_dbg(dev, "claimed by %s, failing probe\n",
+                               dev_name(ndns->claim));
+
+                       return ERR_PTR(-ENXIO);
+               }
+       }
+
+       size = nvdimm_namespace_capacity(ndns);
+       if (size < ND_MIN_NAMESPACE_SIZE) {
+               dev_dbg(&ndns->dev, "%pa, too small must be at least %#x\n",
+                               &size, ND_MIN_NAMESPACE_SIZE);
+               return ERR_PTR(-ENODEV);
+       }
+
+       if (is_namespace_pmem(&ndns->dev)) {
+               struct nd_namespace_pmem *nspm;
+
+               nspm = to_nd_namespace_pmem(&ndns->dev);
+               if (!nspm->uuid) {
+                       dev_dbg(&ndns->dev, "%s: uuid not set\n", __func__);
+                       return ERR_PTR(-ENODEV);
+               }
+       } else if (is_namespace_blk(&ndns->dev)) {
+               struct nd_namespace_blk *nsblk;
+
+               nsblk = to_nd_namespace_blk(&ndns->dev);
+               if (!nd_namespace_blk_validate(nsblk))
+                       return ERR_PTR(-ENODEV);
+       }
+
+       return ndns;
+}
+EXPORT_SYMBOL(nvdimm_namespace_common_probe);
+
 static struct device **create_namespace_io(struct nd_region *nd_region)
 {
        struct nd_namespace_io *nsio;
@@ -1099,7 +1312,7 @@ static struct device **create_namespace_io(struct nd_region *nd_region)
                return NULL;
        }
 
-       dev = &nsio->dev;
+       dev = &nsio->common.dev;
        dev->type = &namespace_io_device_type;
        dev->parent = &nd_region->dev;
        res = &nsio->res;
@@ -1313,7 +1526,7 @@ static struct device **create_namespace_pmem(struct nd_region *nd_region)
        if (!nspm)
                return NULL;
 
-       dev = &nspm->nsio.dev;
+       dev = &nspm->nsio.common.dev;
        dev->type = &namespace_pmem_device_type;
        dev->parent = &nd_region->dev;
        res = &nspm->nsio.res;
@@ -1346,7 +1559,7 @@ static struct device **create_namespace_pmem(struct nd_region *nd_region)
        return devs;
 
  err:
-       namespace_pmem_release(&nspm->nsio.dev);
+       namespace_pmem_release(&nspm->nsio.common.dev);
        return NULL;
 }
 
@@ -1385,7 +1598,7 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region)
        if (!nsblk)
                return NULL;
 
-       dev = &nsblk->dev;
+       dev = &nsblk->common.dev;
        dev->type = &namespace_blk_device_type;
        nsblk->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL);
        if (nsblk->id < 0) {
@@ -1396,7 +1609,7 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region)
        dev->parent = &nd_region->dev;
        dev->groups = nd_namespace_attribute_groups;
 
-       return &nsblk->dev;
+       return &nsblk->common.dev;
 }
 
 void nd_region_create_blk_seed(struct nd_region *nd_region)
@@ -1413,6 +1626,18 @@ void nd_region_create_blk_seed(struct nd_region *nd_region)
                nd_device_register(nd_region->ns_seed);
 }
 
+void nd_region_create_btt_seed(struct nd_region *nd_region)
+{
+       WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
+       nd_region->btt_seed = nd_btt_create(nd_region);
+       /*
+        * Seed creation failures are not fatal, provisioning is simply
+        * disabled until memory becomes available
+        */
+       if (!nd_region->btt_seed)
+               dev_err(&nd_region->dev, "failed to create btt namespace\n");
+}
+
 static struct device **create_namespace_blk(struct nd_region *nd_region)
 {
        struct nd_mapping *nd_mapping = &nd_region->mapping[0];
@@ -1446,7 +1671,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
                                if (!res)
                                        goto err;
                                nd_dbg_dpa(nd_region, ndd, res, "%s assign\n",
-                                       dev_name(&nsblk->dev));
+                                       dev_name(&nsblk->common.dev));
                                break;
                        }
                }
@@ -1462,7 +1687,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
                nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL);
                if (!nsblk)
                        goto err;
-               dev = &nsblk->dev;
+               dev = &nsblk->common.dev;
                dev->type = &namespace_blk_device_type;
                dev->parent = &nd_region->dev;
                dev_set_name(dev, "namespace%d.%d", nd_region->id, count);
@@ -1482,7 +1707,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
                if (!res)
                        goto err;
                nd_dbg_dpa(nd_region, ndd, res, "%s assign\n",
-                               dev_name(&nsblk->dev));
+                               dev_name(&nsblk->common.dev));
        }
 
        dev_dbg(&nd_region->dev, "%s: discovered %d blk namespace%s\n",
@@ -1503,7 +1728,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
                nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL);
                if (!nsblk)
                        goto err;
-               dev = &nsblk->dev;
+               dev = &nsblk->common.dev;
                dev->type = &namespace_blk_device_type;
                dev->parent = &nd_region->dev;
                devs[count++] = dev;
@@ -1514,7 +1739,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
 err:
        for (i = 0; i < count; i++) {
                nsblk = to_nd_namespace_blk(devs[i]);
-               namespace_blk_release(&nsblk->dev);
+               namespace_blk_release(&nsblk->common.dev);
        }
        kfree(devs);
        return NULL;