| 1 | |
| 2 | |
| 3 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
|
| 4 |
|
| 5 | #include <linux/blkdev.h>
|
| 6 | #include <linux/device.h>
|
| 7 | #include <linux/sizes.h>
|
| 8 | #include <linux/slab.h>
|
| 9 | #include <linux/fs.h>
|
| 10 | #include <linux/mm.h>
|
| 11 | #include "nd-core.h"
|
| 12 | #include "btt.h"
|
| 13 | #include "nd.h"
|
| 14 |
|
| 15 | static void nd_btt_release(struct device *dev)
|
| 16 | {
|
| 17 | struct nd_region *nd_region = to_nd_region(dev->parent);
|
| 18 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 19 |
|
| 20 | dev_dbg(dev, "trace\n");
|
| 21 | nd_detach_ndns(&nd_btt->dev, &nd_btt->ndns);
|
| 22 | ida_free(&nd_region->btt_ida, nd_btt->id);
|
| 23 | kfree(nd_btt->uuid);
|
| 24 | kfree(nd_btt);
|
| 25 | }
|
| 26 |
|
| 27 | struct nd_btt *to_nd_btt(struct device *dev)
|
| 28 | {
|
| 29 | struct nd_btt *nd_btt = container_of(dev, struct nd_btt, dev);
|
| 30 |
|
| 31 | WARN_ON(!is_nd_btt(dev));
|
| 32 | return nd_btt;
|
| 33 | }
|
| 34 | EXPORT_SYMBOL(to_nd_btt);
|
| 35 |
|
| 36 | static const unsigned long btt_lbasize_supported[] = { 512, 520, 528,
|
| 37 | 4096, 4104, 4160, 4224, 0 };
|
| 38 |
|
| 39 | static ssize_t sector_size_show(struct device *dev,
|
| 40 | struct device_attribute *attr, char *buf)
|
| 41 | {
|
| 42 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 43 |
|
| 44 | return nd_size_select_show(nd_btt->lbasize, btt_lbasize_supported, buf);
|
| 45 | }
|
| 46 |
|
| 47 | static ssize_t sector_size_store(struct device *dev,
|
| 48 | struct device_attribute *attr, const char *buf, size_t len)
|
| 49 | {
|
| 50 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 51 | ssize_t rc;
|
| 52 |
|
| 53 | guard(device)(dev);
|
| 54 | guard(nvdimm_bus)(dev);
|
| 55 | rc = nd_size_select_store(dev, buf, &nd_btt->lbasize,
|
| 56 | btt_lbasize_supported);
|
| 57 | dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
|
| 58 | buf[len - 1] == '\n' ? "" : "\n");
|
| 59 |
|
| 60 | return rc ? rc : len;
|
| 61 | }
|
| 62 | static DEVICE_ATTR_RW(sector_size);
|
| 63 |
|
| 64 | static ssize_t uuid_show(struct device *dev,
|
| 65 | struct device_attribute *attr, char *buf)
|
| 66 | {
|
| 67 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 68 |
|
| 69 | if (nd_btt->uuid)
|
| 70 | return sprintf(buf, "%pUb\n", nd_btt->uuid);
|
| 71 | return sprintf(buf, "\n");
|
| 72 | }
|
| 73 |
|
| 74 | static ssize_t uuid_store(struct device *dev,
|
| 75 | struct device_attribute *attr, const char *buf, size_t len)
|
| 76 | {
|
| 77 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 78 | ssize_t rc;
|
| 79 |
|
| 80 | device_lock(dev);
|
| 81 | rc = nd_uuid_store(dev, &nd_btt->uuid, buf, len);
|
| 82 | dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
|
| 83 | buf[len - 1] == '\n' ? "" : "\n");
|
| 84 | device_unlock(dev);
|
| 85 |
|
| 86 | return rc ? rc : len;
|
| 87 | }
|
| 88 | static DEVICE_ATTR_RW(uuid);
|
| 89 |
|
| 90 | static ssize_t namespace_show(struct device *dev,
|
| 91 | struct device_attribute *attr, char *buf)
|
| 92 | {
|
| 93 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 94 |
|
| 95 | guard(nvdimm_bus)(dev);
|
| 96 | return sprintf(buf, "%s\n", nd_btt->ndns
|
| 97 | ? dev_name(&nd_btt->ndns->dev) : "");
|
| 98 | }
|
| 99 |
|
| 100 | static ssize_t namespace_store(struct device *dev,
|
| 101 | struct device_attribute *attr, const char *buf, size_t len)
|
| 102 | {
|
| 103 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 104 | ssize_t rc;
|
| 105 |
|
| 106 | guard(device)(dev);
|
| 107 | guard(nvdimm_bus)(dev);
|
| 108 | rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len);
|
| 109 | dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
|
| 110 | buf[len - 1] == '\n' ? "" : "\n");
|
| 111 |
|
| 112 | return rc;
|
| 113 | }
|
| 114 | static DEVICE_ATTR_RW(namespace);
|
| 115 |
|
| 116 | static ssize_t size_show(struct device *dev,
|
| 117 | struct device_attribute *attr, char *buf)
|
| 118 | {
|
| 119 | struct nd_btt *nd_btt = to_nd_btt(dev);
|
| 120 | ssize_t rc;
|
| 121 |
|
| 122 | device_lock(dev);
|
| 123 | if (dev->driver)
|
| 124 | rc = sprintf(buf, "%llu\n", nd_btt->size);
|
| 125 | else {
|
| 126 |
|
| 127 | rc = -ENXIO;
|
| 128 | }
|
| 129 | device_unlock(dev);
|
| 130 |
|
| 131 | return rc;
|
| 132 | }
|
| 133 | static DEVICE_ATTR_RO(size);
|
| 134 |
|
| 135 | static ssize_t log_zero_flags_show(struct device *dev,
|
| 136 | struct device_attribute *attr, char *buf)
|
| 137 | {
|
| 138 | return sprintf(buf, "Y\n");
|
| 139 | }
|
| 140 | static DEVICE_ATTR_RO(log_zero_flags);
|
| 141 |
|
| 142 | static struct attribute *nd_btt_attributes[] = {
|
| 143 | &dev_attr_sector_size.attr,
|
| 144 | &dev_attr_namespace.attr,
|
| 145 | &dev_attr_uuid.attr,
|
| 146 | &dev_attr_size.attr,
|
| 147 | &dev_attr_log_zero_flags.attr,
|
| 148 | NULL,
|
| 149 | };
|
| 150 |
|
| 151 | static struct attribute_group nd_btt_attribute_group = {
|
| 152 | .attrs = nd_btt_attributes,
|
| 153 | };
|
| 154 |
|
| 155 | static const struct attribute_group *nd_btt_attribute_groups[] = {
|
| 156 | &nd_btt_attribute_group,
|
| 157 | &nd_device_attribute_group,
|
| 158 | &nd_numa_attribute_group,
|
| 159 | NULL,
|
| 160 | };
|
| 161 |
|
| 162 | static const struct device_type nd_btt_device_type = {
|
| 163 | .name = "nd_btt",
|
| 164 | .release = nd_btt_release,
|
| 165 | .groups = nd_btt_attribute_groups,
|
| 166 | };
|
| 167 |
|
| 168 | bool is_nd_btt(struct device *dev)
|
| 169 | {
|
| 170 | return dev->type == &nd_btt_device_type;
|
| 171 | }
|
| 172 | EXPORT_SYMBOL(is_nd_btt);
|
| 173 |
|
| 174 | static struct lock_class_key nvdimm_btt_key;
|
| 175 |
|
| 176 | static struct device *__nd_btt_create(struct nd_region *nd_region,
|
| 177 | unsigned long lbasize, uuid_t *uuid,
|
| 178 | struct nd_namespace_common *ndns)
|
| 179 | {
|
| 180 | struct nd_btt *nd_btt;
|
| 181 | struct device *dev;
|
| 182 |
|
| 183 | nd_btt = kzalloc_obj(*nd_btt);
|
| 184 | if (!nd_btt)
|
| 185 | return NULL;
|
| 186 |
|
| 187 | nd_btt->id = ida_alloc(&nd_region->btt_ida, GFP_KERNEL);
|
| 188 | if (nd_btt->id < 0)
|
| 189 | goto out_nd_btt;
|
| 190 |
|
| 191 | nd_btt->lbasize = lbasize;
|
| 192 | if (uuid) {
|
| 193 | uuid = kmemdup(uuid, 16, GFP_KERNEL);
|
| 194 | if (!uuid)
|
| 195 | goto out_put_id;
|
| 196 | }
|
| 197 | nd_btt->uuid = uuid;
|
| 198 | dev = &nd_btt->dev;
|
| 199 | dev_set_name(dev, "btt%d.%d", nd_region->id, nd_btt->id);
|
| 200 | dev->parent = &nd_region->dev;
|
| 201 | dev->type = &nd_btt_device_type;
|
| 202 | device_initialize(&nd_btt->dev);
|
| 203 | lockdep_set_class(&nd_btt->dev.mutex, &nvdimm_btt_key);
|
| 204 | if (ndns && !__nd_attach_ndns(&nd_btt->dev, ndns, &nd_btt->ndns)) {
|
| 205 | dev_dbg(&ndns->dev, "failed, already claimed by %s\n",
|
| 206 | dev_name(ndns->claim));
|
| 207 | put_device(dev);
|
| 208 | return NULL;
|
| 209 | }
|
| 210 | return dev;
|
| 211 |
|
| 212 | out_put_id:
|
| 213 | ida_free(&nd_region->btt_ida, nd_btt->id);
|
| 214 |
|
| 215 | out_nd_btt:
|
| 216 | kfree(nd_btt);
|
| 217 | return NULL;
|
| 218 | }
|
| 219 |
|
| 220 | struct device *nd_btt_create(struct nd_region *nd_region)
|
| 221 | {
|
| 222 | struct device *dev = __nd_btt_create(nd_region, 0, NULL, NULL);
|
| 223 |
|
| 224 | nd_device_register(dev);
|
| 225 | return dev;
|
| 226 | }
|
| 227 |
|
| 228 | |
| 229 | * nd_btt_arena_is_valid - check if the metadata layout is valid
|
| 230 | * @nd_btt: device with BTT geometry and backing device info
|
| 231 | * @super: pointer to the arena's info block being tested
|
| 232 | *
|
| 233 | * Check consistency of the btt info block with itself by validating
|
| 234 | * the checksum, and with the parent namespace by verifying the
|
| 235 | * parent_uuid contained in the info block with the one supplied in.
|
| 236 | *
|
| 237 | * Returns:
|
| 238 | * false for an invalid info block, true for a valid one
|
| 239 |
|
| 240 | bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)
|
| 241 | {
|
| 242 | const uuid_t *ns_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);
|
| 243 | uuid_t parent_uuid;
|
| 244 | u64 checksum;
|
| 245 |
|
| 246 | if (memcmp(super->signature, BTT_SIG, BTT_SIG_LEN) != 0)
|
| 247 | return false;
|
| 248 |
|
| 249 | import_uuid(&parent_uuid, super->parent_uuid);
|
| 250 | if (!uuid_is_null(&parent_uuid))
|
| 251 | if (!uuid_equal(&parent_uuid, ns_uuid))
|
| 252 | return false;
|
| 253 |
|
| 254 | checksum = le64_to_cpu(super->checksum);
|
| 255 | super->checksum = 0;
|
| 256 | if (checksum != nd_sb_checksum((struct nd_gen_sb *) super))
|
| 257 | return false;
|
| 258 | super->checksum = cpu_to_le64(checksum);
|
| 259 |
|
| 260 |
|
| 261 | if ((le32_to_cpu(super->flags) & IB_FLAG_ERROR_MASK) != 0)
|
| 262 | dev_info(&nd_btt->dev, "Found arena with an error flag\n");
|
| 263 |
|
| 264 | return true;
|
| 265 | }
|
| 266 | EXPORT_SYMBOL(nd_btt_arena_is_valid);
|
| 267 |
|
| 268 | int nd_btt_version(struct nd_btt *nd_btt, struct nd_namespace_common *ndns,
|
| 269 | struct btt_sb *btt_sb)
|
| 270 | {
|
| 271 | if (ndns->claim_class == NVDIMM_CCLASS_BTT2) {
|
| 272 |
|
| 273 | nd_btt->initial_offset = 0;
|
| 274 | nd_btt->version_major = 2;
|
| 275 | nd_btt->version_minor = 0;
|
| 276 | if (nvdimm_read_bytes(ndns, 0, btt_sb, sizeof(*btt_sb), 0))
|
| 277 | return -ENXIO;
|
| 278 | if (!nd_btt_arena_is_valid(nd_btt, btt_sb))
|
| 279 | return -ENODEV;
|
| 280 | if ((le16_to_cpu(btt_sb->version_major) != 2) ||
|
| 281 | (le16_to_cpu(btt_sb->version_minor) != 0))
|
| 282 | return -ENODEV;
|
| 283 | } else {
|
| 284 | |
| 285 | * Probe/setup for BTT v1.1 (NVDIMM_CCLASS_NONE or
|
| 286 | * NVDIMM_CCLASS_BTT)
|
| 287 |
|
| 288 | nd_btt->initial_offset = SZ_4K;
|
| 289 | nd_btt->version_major = 1;
|
| 290 | nd_btt->version_minor = 1;
|
| 291 | if (nvdimm_read_bytes(ndns, SZ_4K, btt_sb, sizeof(*btt_sb), 0))
|
| 292 | return -ENXIO;
|
| 293 | if (!nd_btt_arena_is_valid(nd_btt, btt_sb))
|
| 294 | return -ENODEV;
|
| 295 | if ((le16_to_cpu(btt_sb->version_major) != 1) ||
|
| 296 | (le16_to_cpu(btt_sb->version_minor) != 1))
|
| 297 | return -ENODEV;
|
| 298 | }
|
| 299 | return 0;
|
| 300 | }
|
| 301 | EXPORT_SYMBOL(nd_btt_version);
|
| 302 |
|
| 303 | static int __nd_btt_probe(struct nd_btt *nd_btt,
|
| 304 | struct nd_namespace_common *ndns, struct btt_sb *btt_sb)
|
| 305 | {
|
| 306 | int rc;
|
| 307 |
|
| 308 | if (!btt_sb || !ndns || !nd_btt)
|
| 309 | return -ENODEV;
|
| 310 |
|
| 311 | if (nvdimm_namespace_capacity(ndns) < SZ_16M)
|
| 312 | return -ENXIO;
|
| 313 |
|
| 314 | rc = nd_btt_version(nd_btt, ndns, btt_sb);
|
| 315 | if (rc < 0)
|
| 316 | return rc;
|
| 317 |
|
| 318 | nd_btt->lbasize = le32_to_cpu(btt_sb->external_lbasize);
|
| 319 | nd_btt->uuid = kmemdup(&btt_sb->uuid, sizeof(uuid_t), GFP_KERNEL);
|
| 320 | if (!nd_btt->uuid)
|
| 321 | return -ENOMEM;
|
| 322 |
|
| 323 | nd_device_register(&nd_btt->dev);
|
| 324 |
|
| 325 | return 0;
|
| 326 | }
|
| 327 |
|
| 328 | int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns)
|
| 329 | {
|
| 330 | int rc;
|
| 331 | struct device *btt_dev;
|
| 332 | struct btt_sb *btt_sb;
|
| 333 | struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
|
| 334 |
|
| 335 | if (ndns->force_raw)
|
| 336 | return -ENODEV;
|
| 337 |
|
| 338 | switch (ndns->claim_class) {
|
| 339 | case NVDIMM_CCLASS_NONE:
|
| 340 | case NVDIMM_CCLASS_BTT:
|
| 341 | case NVDIMM_CCLASS_BTT2:
|
| 342 | break;
|
| 343 | default:
|
| 344 | return -ENODEV;
|
| 345 | }
|
| 346 |
|
| 347 | scoped_guard(nvdimm_bus, &ndns->dev)
|
| 348 | btt_dev = __nd_btt_create(nd_region, 0, NULL, ndns);
|
| 349 | if (!btt_dev)
|
| 350 | return -ENOMEM;
|
| 351 | btt_sb = devm_kzalloc(dev, sizeof(*btt_sb), GFP_KERNEL);
|
| 352 | rc = __nd_btt_probe(to_nd_btt(btt_dev), ndns, btt_sb);
|
| 353 | dev_dbg(dev, "btt: %s\n", rc == 0 ? dev_name(btt_dev) : "<none>");
|
| 354 | if (rc < 0) {
|
| 355 | struct nd_btt *nd_btt = to_nd_btt(btt_dev);
|
| 356 |
|
| 357 | nd_detach_ndns(btt_dev, &nd_btt->ndns);
|
| 358 | put_device(btt_dev);
|
| 359 | }
|
| 360 |
|
| 361 | return rc;
|
| 362 | }
|
| 363 | EXPORT_SYMBOL(nd_btt_probe);
|