libnvdimm, nvdimm: dimm driver and base libnvdimm device-driver infrastructure

* Implement the device-model infrastructure for loading modules and
  attaching drivers to nvdimm devices.  This is a simple association of a
  nd-device-type number with a driver that has a bitmask of supported
  device types.  To facilitate userspace bind/unbind operations 'modalias'
  and 'devtype', that also appear in the uevent, are added as generic
  sysfs attributes for all nvdimm devices.  The reason for the device-type
  number is to support sub-types within a given parent devtype, be it a
  vendor-specific sub-type or otherwise.

* The first consumer of this infrastructure is the driver
  for dimm devices.  It simply uses control messages to retrieve and
  store the configuration-data image (label set) from each dimm.

Note: nd_device_register() arranges for asynchronous registration of
      nvdimm bus devices by default.

Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Neil Brown <neilb@suse.de>
Acked-by: Christoph Hellwig <hch@lst.de>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
Dan Williams
2015-05-31 14:41:48 -04:00
parent 62232e45f4
commit 4d88a97aa9
11 changed files with 527 additions and 15 deletions

View File

@@ -11,6 +11,7 @@
* General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/ndctl.h>
#include <linux/slab.h>
@@ -18,9 +19,115 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include "nd-core.h"
#include "nd.h"
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)
{
struct nvdimm *nvdimm;
if (!ndd)
return -EINVAL;
nvdimm = to_nvdimm(ndd->dev);
if (!nvdimm->dsm_mask)
return -ENXIO;
if (!test_bit(ND_CMD_GET_CONFIG_DATA, nvdimm->dsm_mask))
return -ENXIO;
return 0;
}
static int validate_dimm(struct nvdimm_drvdata *ndd)
{
int rc = __validate_dimm(ndd);
if (rc && ndd)
dev_dbg(ndd->dev, "%pf: %s error: %d\n",
__builtin_return_address(0), __func__, rc);
return rc;
}
/**
* nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
* @nvdimm: dimm to initialize
*/
int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
{
struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
struct nvdimm_bus_descriptor *nd_desc;
int rc = validate_dimm(ndd);
if (rc)
return rc;
if (cmd->config_size)
return 0; /* already valid */
memset(cmd, 0, sizeof(*cmd));
nd_desc = nvdimm_bus->nd_desc;
return nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd));
}
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
{
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
struct nd_cmd_get_config_data_hdr *cmd;
struct nvdimm_bus_descriptor *nd_desc;
int rc = validate_dimm(ndd);
u32 max_cmd_size, config_size;
size_t offset;
if (rc)
return rc;
if (ndd->data)
return 0;
if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0)
return -ENXIO;
ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL);
if (!ndd->data)
ndd->data = vmalloc(ndd->nsarea.config_size);
if (!ndd->data)
return -ENOMEM;
max_cmd_size = min_t(u32, PAGE_SIZE, ndd->nsarea.max_xfer);
cmd = kzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
nd_desc = nvdimm_bus->nd_desc;
for (config_size = ndd->nsarea.config_size, offset = 0;
config_size; config_size -= cmd->in_length,
offset += cmd->in_length) {
cmd->in_length = min(config_size, max_cmd_size);
cmd->in_offset = offset;
rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
ND_CMD_GET_CONFIG_DATA, cmd,
cmd->in_length + sizeof(*cmd));
if (rc || cmd->status) {
rc = -ENXIO;
break;
}
memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length);
}
dev_dbg(ndd->dev, "%s: len: %zu rc: %d\n", __func__, offset, rc);
kfree(cmd);
return rc;
}
static void nvdimm_release(struct device *dev)
{
struct nvdimm *nvdimm = to_nvdimm(dev);
@@ -111,14 +218,33 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
dev_set_name(dev, "nmem%d", nvdimm->id);
dev->parent = &nvdimm_bus->dev;
dev->type = &nvdimm_device_type;
dev->bus = &nvdimm_bus_type;
dev->devt = MKDEV(nvdimm_major, nvdimm->id);
dev->groups = groups;
if (device_register(dev) != 0) {
put_device(dev);
return NULL;
}
nd_device_register(dev);
return nvdimm;
}
EXPORT_SYMBOL_GPL(nvdimm_create);
static int count_dimms(struct device *dev, void *c)
{
int *count = c;
if (is_nvdimm(dev))
(*count)++;
return 0;
}
int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
{
int count = 0;
/* Flush any possible dimm registration failures */
nd_synchronize();
device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count);
if (count != dimm_count)
return -ENXIO;
return 0;
}
EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);