4b27db7e26
ACPI 6.2 adds support for named methods to access the label storage area of an NVDIMM. We prefer these new methods if available and otherwise fallback to the NVDIMM_FAMILY_INTEL _DSMs. The kernel ioctls, ND_IOCTL_{GET,SET}_CONFIG_{SIZE,DATA}, remain generic and the driver translates the 'package' payloads into the NVDIMM_FAMILY_INTEL 'buffer' format to maintain compatibility with existing userspace and keep the output buffer parsing code in the driver common. The output payloads are mostly compatible save for the 'label area locked' status that moves from the 'config_size' (_LSI) command to the 'config_read' (_LSR) command status. Cc: Jeff Moyer <jmoyer@redhat.com> Cc: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
121 lines
2.5 KiB
C
121 lines
2.5 KiB
C
/*
|
|
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/ndctl.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/nd.h>
|
|
#include "label.h"
|
|
#include "nd.h"
|
|
|
|
static int nvdimm_probe(struct device *dev)
|
|
{
|
|
struct nvdimm_drvdata *ndd;
|
|
int rc;
|
|
|
|
rc = nvdimm_check_config_data(dev);
|
|
if (rc) {
|
|
/* not required for non-aliased nvdimm, ex. NVDIMM-N */
|
|
if (rc == -ENOTTY)
|
|
rc = 0;
|
|
return rc;
|
|
}
|
|
|
|
ndd = kzalloc(sizeof(*ndd), GFP_KERNEL);
|
|
if (!ndd)
|
|
return -ENOMEM;
|
|
|
|
dev_set_drvdata(dev, ndd);
|
|
ndd->dpa.name = dev_name(dev);
|
|
ndd->ns_current = -1;
|
|
ndd->ns_next = -1;
|
|
ndd->dpa.start = 0;
|
|
ndd->dpa.end = -1;
|
|
ndd->dev = dev;
|
|
get_device(dev);
|
|
kref_init(&ndd->kref);
|
|
|
|
rc = nvdimm_init_nsarea(ndd);
|
|
if (rc == -EACCES)
|
|
nvdimm_set_locked(dev);
|
|
if (rc)
|
|
goto err;
|
|
|
|
rc = nvdimm_init_config_data(ndd);
|
|
if (rc == -EACCES)
|
|
nvdimm_set_locked(dev);
|
|
if (rc)
|
|
goto err;
|
|
|
|
dev_dbg(dev, "config data size: %d\n", ndd->nsarea.config_size);
|
|
|
|
nvdimm_bus_lock(dev);
|
|
ndd->ns_current = nd_label_validate(ndd);
|
|
ndd->ns_next = nd_label_next_nsindex(ndd->ns_current);
|
|
nd_label_copy(ndd, to_next_namespace_index(ndd),
|
|
to_current_namespace_index(ndd));
|
|
rc = nd_label_reserve_dpa(ndd);
|
|
if (ndd->ns_current >= 0)
|
|
nvdimm_set_aliasing(dev);
|
|
nvdimm_clear_locked(dev);
|
|
nvdimm_bus_unlock(dev);
|
|
|
|
if (rc)
|
|
goto err;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
put_ndd(ndd);
|
|
return rc;
|
|
}
|
|
|
|
static int nvdimm_remove(struct device *dev)
|
|
{
|
|
struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
|
|
|
|
if (!ndd)
|
|
return 0;
|
|
|
|
nvdimm_bus_lock(dev);
|
|
dev_set_drvdata(dev, NULL);
|
|
nvdimm_bus_unlock(dev);
|
|
put_ndd(ndd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct nd_device_driver nvdimm_driver = {
|
|
.probe = nvdimm_probe,
|
|
.remove = nvdimm_remove,
|
|
.drv = {
|
|
.name = "nvdimm",
|
|
},
|
|
.type = ND_DRIVER_DIMM,
|
|
};
|
|
|
|
int __init nvdimm_init(void)
|
|
{
|
|
return nd_driver_register(&nvdimm_driver);
|
|
}
|
|
|
|
void nvdimm_exit(void)
|
|
{
|
|
driver_unregister(&nvdimm_driver.drv);
|
|
}
|
|
|
|
MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DIMM);
|