source: src/linux/universal/linux-4.9/drivers/nvdimm/dimm_devs.c @ 31885

Last change on this file since 31885 was 31885, checked in by brainslayer, 3 months ago

update

File size: 15.6 KB
Line 
1/*
2 * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 * General Public License for more details.
12 */
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14#include <linux/vmalloc.h>
15#include <linux/device.h>
16#include <linux/ndctl.h>
17#include <linux/slab.h>
18#include <linux/io.h>
19#include <linux/fs.h>
20#include <linux/mm.h>
21#include "nd-core.h"
22#include "label.h"
23#include "nd.h"
24
25static DEFINE_IDA(dimm_ida);
26
27/*
28 * Retrieve bus and dimm handle and return if this bus supports
29 * get_config_data commands
30 */
31int nvdimm_check_config_data(struct device *dev)
32{
33        struct nvdimm *nvdimm = to_nvdimm(dev);
34
35        if (!nvdimm->cmd_mask ||
36            !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
37                if (nvdimm->flags & NDD_ALIASING)
38                        return -ENXIO;
39                else
40                        return -ENOTTY;
41        }
42
43        return 0;
44}
45
46static int validate_dimm(struct nvdimm_drvdata *ndd)
47{
48        int rc;
49
50        if (!ndd)
51                return -EINVAL;
52
53        rc = nvdimm_check_config_data(ndd->dev);
54        if (rc)
55                dev_dbg(ndd->dev, "%pf: %s error: %d\n",
56                                __builtin_return_address(0), __func__, rc);
57        return rc;
58}
59
60/**
61 * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
62 * @nvdimm: dimm to initialize
63 */
64int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
65{
66        struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
67        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
68        struct nvdimm_bus_descriptor *nd_desc;
69        int rc = validate_dimm(ndd);
70
71        if (rc)
72                return rc;
73
74        if (cmd->config_size)
75                return 0; /* already valid */
76
77        memset(cmd, 0, sizeof(*cmd));
78        nd_desc = nvdimm_bus->nd_desc;
79        return nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
80                        ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), NULL);
81}
82
83int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
84{
85        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
86        struct nd_cmd_get_config_data_hdr *cmd;
87        struct nvdimm_bus_descriptor *nd_desc;
88        int rc = validate_dimm(ndd);
89        u32 max_cmd_size, config_size;
90        size_t offset;
91
92        if (rc)
93                return rc;
94
95        if (ndd->data)
96                return 0;
97
98        if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0
99                        || ndd->nsarea.config_size < ND_LABEL_MIN_SIZE) {
100                dev_dbg(ndd->dev, "failed to init config data area: (%d:%d)\n",
101                                ndd->nsarea.max_xfer, ndd->nsarea.config_size);
102                return -ENXIO;
103        }
104
105        ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL);
106        if (!ndd->data)
107                ndd->data = vmalloc(ndd->nsarea.config_size);
108
109        if (!ndd->data)
110                return -ENOMEM;
111
112        max_cmd_size = min_t(u32, PAGE_SIZE, ndd->nsarea.max_xfer);
113        cmd = kzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
114        if (!cmd)
115                return -ENOMEM;
116
117        nd_desc = nvdimm_bus->nd_desc;
118        for (config_size = ndd->nsarea.config_size, offset = 0;
119                        config_size; config_size -= cmd->in_length,
120                        offset += cmd->in_length) {
121                cmd->in_length = min(config_size, max_cmd_size);
122                cmd->in_offset = offset;
123                rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
124                                ND_CMD_GET_CONFIG_DATA, cmd,
125                                cmd->in_length + sizeof(*cmd), NULL);
126                if (rc || cmd->status) {
127                        rc = -ENXIO;
128                        break;
129                }
130                memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length);
131        }
132        dev_dbg(ndd->dev, "%s: len: %zu rc: %d\n", __func__, offset, rc);
133        kfree(cmd);
134
135        return rc;
136}
137
138int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
139                void *buf, size_t len)
140{
141        int rc = validate_dimm(ndd);
142        size_t max_cmd_size, buf_offset;
143        struct nd_cmd_set_config_hdr *cmd;
144        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
145        struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
146
147        if (rc)
148                return rc;
149
150        if (!ndd->data)
151                return -ENXIO;
152
153        if (offset + len > ndd->nsarea.config_size)
154                return -ENXIO;
155
156        max_cmd_size = min_t(u32, PAGE_SIZE, len);
157        max_cmd_size = min_t(u32, max_cmd_size, ndd->nsarea.max_xfer);
158        cmd = kzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
159        if (!cmd)
160                return -ENOMEM;
161
162        for (buf_offset = 0; len; len -= cmd->in_length,
163                        buf_offset += cmd->in_length) {
164                size_t cmd_size;
165                u32 *status;
166
167                cmd->in_offset = offset + buf_offset;
168                cmd->in_length = min(max_cmd_size, len);
169                memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
170
171                /* status is output in the last 4-bytes of the command buffer */
172                cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
173                status = ((void *) cmd) + cmd_size - sizeof(u32);
174
175                rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
176                                ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL);
177                if (rc || *status) {
178                        rc = rc ? rc : -ENXIO;
179                        break;
180                }
181        }
182        kfree(cmd);
183
184        return rc;
185}
186
187static void nvdimm_release(struct device *dev)
188{
189        struct nvdimm *nvdimm = to_nvdimm(dev);
190
191        ida_simple_remove(&dimm_ida, nvdimm->id);
192        kfree(nvdimm);
193}
194
195static struct device_type nvdimm_device_type = {
196        .name = "nvdimm",
197        .release = nvdimm_release,
198};
199
200bool is_nvdimm(struct device *dev)
201{
202        return dev->type == &nvdimm_device_type;
203}
204
205struct nvdimm *to_nvdimm(struct device *dev)
206{
207        struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
208
209        WARN_ON(!is_nvdimm(dev));
210        return nvdimm;
211}
212EXPORT_SYMBOL_GPL(to_nvdimm);
213
214struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr)
215{
216        struct nd_region *nd_region = &ndbr->nd_region;
217        struct nd_mapping *nd_mapping = &nd_region->mapping[0];
218
219        return nd_mapping->nvdimm;
220}
221EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm);
222
223struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
224{
225        struct nvdimm *nvdimm = nd_mapping->nvdimm;
226
227        WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
228
229        return dev_get_drvdata(&nvdimm->dev);
230}
231EXPORT_SYMBOL(to_ndd);
232
233void nvdimm_drvdata_release(struct kref *kref)
234{
235        struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
236        struct device *dev = ndd->dev;
237        struct resource *res, *_r;
238
239        dev_dbg(dev, "%s\n", __func__);
240
241        nvdimm_bus_lock(dev);
242        for_each_dpa_resource_safe(ndd, res, _r)
243                nvdimm_free_dpa(ndd, res);
244        nvdimm_bus_unlock(dev);
245
246        kvfree(ndd->data);
247        kfree(ndd);
248        put_device(dev);
249}
250
251void get_ndd(struct nvdimm_drvdata *ndd)
252{
253        kref_get(&ndd->kref);
254}
255
256void put_ndd(struct nvdimm_drvdata *ndd)
257{
258        if (ndd)
259                kref_put(&ndd->kref, nvdimm_drvdata_release);
260}
261
262const char *nvdimm_name(struct nvdimm *nvdimm)
263{
264        return dev_name(&nvdimm->dev);
265}
266EXPORT_SYMBOL_GPL(nvdimm_name);
267
268struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
269{
270        return &nvdimm->dev.kobj;
271}
272EXPORT_SYMBOL_GPL(nvdimm_kobj);
273
274unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
275{
276        return nvdimm->cmd_mask;
277}
278EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
279
280void *nvdimm_provider_data(struct nvdimm *nvdimm)
281{
282        if (nvdimm)
283                return nvdimm->provider_data;
284        return NULL;
285}
286EXPORT_SYMBOL_GPL(nvdimm_provider_data);
287
288static ssize_t commands_show(struct device *dev,
289                struct device_attribute *attr, char *buf)
290{
291        struct nvdimm *nvdimm = to_nvdimm(dev);
292        int cmd, len = 0;
293
294        if (!nvdimm->cmd_mask)
295                return sprintf(buf, "\n");
296
297        for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
298                len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
299        len += sprintf(buf + len, "\n");
300        return len;
301}
302static DEVICE_ATTR_RO(commands);
303
304static ssize_t state_show(struct device *dev, struct device_attribute *attr,
305                char *buf)
306{
307        struct nvdimm *nvdimm = to_nvdimm(dev);
308
309        /*
310         * The state may be in the process of changing, userspace should
311         * quiesce probing if it wants a static answer
312         */
313        nvdimm_bus_lock(dev);
314        nvdimm_bus_unlock(dev);
315        return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy)
316                        ? "active" : "idle");
317}
318static DEVICE_ATTR_RO(state);
319
320static ssize_t available_slots_show(struct device *dev,
321                struct device_attribute *attr, char *buf)
322{
323        struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
324        ssize_t rc;
325        u32 nfree;
326
327        if (!ndd)
328                return -ENXIO;
329
330        nvdimm_bus_lock(dev);
331        nfree = nd_label_nfree(ndd);
332        if (nfree - 1 > nfree) {
333                dev_WARN_ONCE(dev, 1, "we ate our last label?\n");
334                nfree = 0;
335        } else
336                nfree--;
337        rc = sprintf(buf, "%d\n", nfree);
338        nvdimm_bus_unlock(dev);
339        return rc;
340}
341static DEVICE_ATTR_RO(available_slots);
342
343static struct attribute *nvdimm_attributes[] = {
344        &dev_attr_state.attr,
345        &dev_attr_commands.attr,
346        &dev_attr_available_slots.attr,
347        NULL,
348};
349
350struct attribute_group nvdimm_attribute_group = {
351        .attrs = nvdimm_attributes,
352};
353EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
354
355struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
356                const struct attribute_group **groups, unsigned long flags,
357                unsigned long cmd_mask, int num_flush,
358                struct resource *flush_wpq)
359{
360        struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
361        struct device *dev;
362
363        if (!nvdimm)
364                return NULL;
365
366        nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
367        if (nvdimm->id < 0) {
368                kfree(nvdimm);
369                return NULL;
370        }
371        nvdimm->provider_data = provider_data;
372        nvdimm->flags = flags;
373        nvdimm->cmd_mask = cmd_mask;
374        nvdimm->num_flush = num_flush;
375        nvdimm->flush_wpq = flush_wpq;
376        atomic_set(&nvdimm->busy, 0);
377        dev = &nvdimm->dev;
378        dev_set_name(dev, "nmem%d", nvdimm->id);
379        dev->parent = &nvdimm_bus->dev;
380        dev->type = &nvdimm_device_type;
381        dev->devt = MKDEV(nvdimm_major, nvdimm->id);
382        dev->groups = groups;
383        nd_device_register(dev);
384
385        return nvdimm;
386}
387EXPORT_SYMBOL_GPL(nvdimm_create);
388
389int alias_dpa_busy(struct device *dev, void *data)
390{
391        resource_size_t map_end, blk_start, new;
392        struct blk_alloc_info *info = data;
393        struct nd_mapping *nd_mapping;
394        struct nd_region *nd_region;
395        struct nvdimm_drvdata *ndd;
396        struct resource *res;
397        int i;
398
399        if (!is_nd_pmem(dev))
400                return 0;
401
402        nd_region = to_nd_region(dev);
403        for (i = 0; i < nd_region->ndr_mappings; i++) {
404                nd_mapping  = &nd_region->mapping[i];
405                if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
406                        break;
407        }
408
409        if (i >= nd_region->ndr_mappings)
410                return 0;
411
412        ndd = to_ndd(nd_mapping);
413        map_end = nd_mapping->start + nd_mapping->size - 1;
414        blk_start = nd_mapping->start;
415
416        /*
417         * In the allocation case ->res is set to free space that we are
418         * looking to validate against PMEM aliasing collision rules
419         * (i.e. BLK is allocated after all aliased PMEM).
420         */
421        if (info->res) {
422                if (info->res->start >= nd_mapping->start
423                                && info->res->start < map_end)
424                        /* pass */;
425                else
426                        return 0;
427        }
428
429 retry:
430        /*
431         * Find the free dpa from the end of the last pmem allocation to
432         * the end of the interleave-set mapping.
433         */
434        for_each_dpa_resource(ndd, res) {
435                if (strncmp(res->name, "pmem", 4) != 0)
436                        continue;
437                if ((res->start >= blk_start && res->start < map_end)
438                                || (res->end >= blk_start
439                                        && res->end <= map_end)) {
440                        new = max(blk_start, min(map_end + 1, res->end + 1));
441                        if (new != blk_start) {
442                                blk_start = new;
443                                goto retry;
444                        }
445                }
446        }
447
448        /* update the free space range with the probed blk_start */
449        if (info->res && blk_start > info->res->start) {
450                info->res->start = max(info->res->start, blk_start);
451                if (info->res->start > info->res->end)
452                        info->res->end = info->res->start - 1;
453                return 1;
454        }
455
456        info->available -= blk_start - nd_mapping->start;
457
458        return 0;
459}
460
461/**
462 * nd_blk_available_dpa - account the unused dpa of BLK region
463 * @nd_mapping: container of dpa-resource-root + labels
464 *
465 * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but
466 * we arrange for them to never start at an lower dpa than the last
467 * PMEM allocation in an aliased region.
468 */
469resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
470{
471        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
472        struct nd_mapping *nd_mapping = &nd_region->mapping[0];
473        struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
474        struct blk_alloc_info info = {
475                .nd_mapping = nd_mapping,
476                .available = nd_mapping->size,
477                .res = NULL,
478        };
479        struct resource *res;
480
481        if (!ndd)
482                return 0;
483
484        device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy);
485
486        /* now account for busy blk allocations in unaliased dpa */
487        for_each_dpa_resource(ndd, res) {
488                if (strncmp(res->name, "blk", 3) != 0)
489                        continue;
490                info.available -= resource_size(res);
491        }
492
493        return info.available;
494}
495
496/**
497 * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
498 * @nd_mapping: container of dpa-resource-root + labels
499 * @nd_region: constrain available space check to this reference region
500 * @overlap: calculate available space assuming this level of overlap
501 *
502 * Validate that a PMEM label, if present, aligns with the start of an
503 * interleave set and truncate the available size at the lowest BLK
504 * overlap point.
505 *
506 * The expectation is that this routine is called multiple times as it
507 * probes for the largest BLK encroachment for any single member DIMM of
508 * the interleave set.  Once that value is determined the PMEM-limit for
509 * the set can be established.
510 */
511resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
512                struct nd_mapping *nd_mapping, resource_size_t *overlap)
513{
514        resource_size_t map_start, map_end, busy = 0, available, blk_start;
515        struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
516        struct resource *res;
517        const char *reason;
518
519        if (!ndd)
520                return 0;
521
522        map_start = nd_mapping->start;
523        map_end = map_start + nd_mapping->size - 1;
524        blk_start = max(map_start, map_end + 1 - *overlap);
525        for_each_dpa_resource(ndd, res) {
526                if (res->start >= map_start && res->start < map_end) {
527                        if (strncmp(res->name, "blk", 3) == 0)
528                                blk_start = min(blk_start,
529                                                max(map_start, res->start));
530                        else if (res->end > map_end) {
531                                reason = "misaligned to iset";
532                                goto err;
533                        } else
534                                busy += resource_size(res);
535                } else if (res->end >= map_start && res->end <= map_end) {
536                        if (strncmp(res->name, "blk", 3) == 0) {
537                                /*
538                                 * If a BLK allocation overlaps the start of
539                                 * PMEM the entire interleave set may now only
540                                 * be used for BLK.
541                                 */
542                                blk_start = map_start;
543                        } else
544                                busy += resource_size(res);
545                } else if (map_start > res->start && map_start < res->end) {
546                        /* total eclipse of the mapping */
547                        busy += nd_mapping->size;
548                        blk_start = map_start;
549                }
550        }
551
552        *overlap = map_end + 1 - blk_start;
553        available = blk_start - map_start;
554        if (busy < available)
555                return available - busy;
556        return 0;
557
558 err:
559        nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
560        return 0;
561}
562
563void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
564{
565        WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
566        kfree(res->name);
567        __release_region(&ndd->dpa, res->start, resource_size(res));
568}
569
570struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
571                struct nd_label_id *label_id, resource_size_t start,
572                resource_size_t n)
573{
574        char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
575        struct resource *res;
576
577        if (!name)
578                return NULL;
579
580        WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
581        res = __request_region(&ndd->dpa, start, n, name, 0);
582        if (!res)
583                kfree(name);
584        return res;
585}
586
587/**
588 * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
589 * @nvdimm: container of dpa-resource-root + labels
590 * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid>
591 */
592resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
593                struct nd_label_id *label_id)
594{
595        resource_size_t allocated = 0;
596        struct resource *res;
597
598        for_each_dpa_resource(ndd, res)
599                if (strcmp(res->name, label_id->id) == 0)
600                        allocated += resource_size(res);
601
602        return allocated;
603}
604
605static int count_dimms(struct device *dev, void *c)
606{
607        int *count = c;
608
609        if (is_nvdimm(dev))
610                (*count)++;
611        return 0;
612}
613
614int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
615{
616        int count = 0;
617        /* Flush any possible dimm registration failures */
618        nd_synchronize();
619
620        device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
621        dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count);
622        if (count != dimm_count)
623                return -ENXIO;
624        return 0;
625}
626EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
627
628void __exit nvdimm_devs_exit(void)
629{
630        ida_destroy(&dimm_ida);
631}
Note: See TracBrowser for help on using the repository browser.