Merge branch 'for-4.7/dsm' into libnvdimm-for-next

This commit is contained in:
Dan Williams 2016-05-18 10:06:59 -07:00
commit 1f716d05f8
11 changed files with 284 additions and 59 deletions

View File

@ -45,6 +45,11 @@ module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(scrub_overflow_abort, MODULE_PARM_DESC(scrub_overflow_abort,
"Number of times we overflow ARS results before abort"); "Number of times we overflow ARS results before abort");
static bool disable_vendor_specific;
module_param(disable_vendor_specific, bool, S_IRUGO);
MODULE_PARM_DESC(disable_vendor_specific,
"Limit commands to the publicly specified set\n");
static struct workqueue_struct *nfit_wq; static struct workqueue_struct *nfit_wq;
struct nfit_table_prev { struct nfit_table_prev {
@ -171,33 +176,46 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
unsigned int buf_len, int *cmd_rc) unsigned int buf_len, int *cmd_rc)
{ {
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
const struct nd_cmd_desc *desc = NULL;
union acpi_object in_obj, in_buf, *out_obj; union acpi_object in_obj, in_buf, *out_obj;
const struct nd_cmd_desc *desc = NULL;
struct device *dev = acpi_desc->dev; struct device *dev = acpi_desc->dev;
struct nd_cmd_pkg *call_pkg = NULL;
const char *cmd_name, *dimm_name; const char *cmd_name, *dimm_name;
unsigned long dsm_mask; unsigned long cmd_mask, dsm_mask;
acpi_handle handle; acpi_handle handle;
unsigned int func;
const u8 *uuid; const u8 *uuid;
u32 offset; u32 offset;
int rc, i; int rc, i;
func = cmd;
if (cmd == ND_CMD_CALL) {
call_pkg = buf;
func = call_pkg->nd_command;
}
if (nvdimm) { if (nvdimm) {
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
struct acpi_device *adev = nfit_mem->adev; struct acpi_device *adev = nfit_mem->adev;
if (!adev) if (!adev)
return -ENOTTY; return -ENOTTY;
if (call_pkg && nfit_mem->family != call_pkg->nd_family)
return -ENOTTY;
dimm_name = nvdimm_name(nvdimm); dimm_name = nvdimm_name(nvdimm);
cmd_name = nvdimm_cmd_name(cmd); cmd_name = nvdimm_cmd_name(cmd);
cmd_mask = nvdimm_cmd_mask(nvdimm);
dsm_mask = nfit_mem->dsm_mask; dsm_mask = nfit_mem->dsm_mask;
desc = nd_cmd_dimm_desc(cmd); desc = nd_cmd_dimm_desc(cmd);
uuid = to_nfit_uuid(NFIT_DEV_DIMM); uuid = to_nfit_uuid(nfit_mem->family);
handle = adev->handle; handle = adev->handle;
} else { } else {
struct acpi_device *adev = to_acpi_dev(acpi_desc); struct acpi_device *adev = to_acpi_dev(acpi_desc);
cmd_name = nvdimm_bus_cmd_name(cmd); cmd_name = nvdimm_bus_cmd_name(cmd);
dsm_mask = nd_desc->dsm_mask; cmd_mask = nd_desc->cmd_mask;
dsm_mask = cmd_mask;
desc = nd_cmd_bus_desc(cmd); desc = nd_cmd_bus_desc(cmd);
uuid = to_nfit_uuid(NFIT_DEV_BUS); uuid = to_nfit_uuid(NFIT_DEV_BUS);
handle = adev->handle; handle = adev->handle;
@ -207,7 +225,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
return -ENOTTY; return -ENOTTY;
if (!test_bit(cmd, &dsm_mask)) if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask))
return -ENOTTY; return -ENOTTY;
in_obj.type = ACPI_TYPE_PACKAGE; in_obj.type = ACPI_TYPE_PACKAGE;
@ -222,21 +240,44 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc, in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
i, buf); i, buf);
if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) { if (call_pkg) {
dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__, /* skip over package wrapper */
dimm_name, cmd_name, in_buf.buffer.length); in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, in_buf.buffer.length = call_pkg->nd_size_in;
4, in_buf.buffer.pointer, min_t(u32, 128,
in_buf.buffer.length), true);
} }
out_obj = acpi_evaluate_dsm(handle, uuid, 1, cmd, &in_obj); if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n",
__func__, dimm_name, cmd, func,
in_buf.buffer.length);
print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
in_buf.buffer.pointer,
min_t(u32, 256, in_buf.buffer.length), true);
}
out_obj = acpi_evaluate_dsm(handle, uuid, 1, func, &in_obj);
if (!out_obj) { if (!out_obj) {
dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name, dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
cmd_name); cmd_name);
return -EINVAL; return -EINVAL;
} }
if (call_pkg) {
call_pkg->nd_fw_size = out_obj->buffer.length;
memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
out_obj->buffer.pointer,
min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
ACPI_FREE(out_obj);
/*
* Need to support FW function w/o known size in advance.
* Caller can determine required size based upon nd_fw_size.
* If we return an error (like elsewhere) then caller wouldn't
* be able to rely upon data returned to make calculation.
*/
return 0;
}
if (out_obj->package.type != ACPI_TYPE_BUFFER) { if (out_obj->package.type != ACPI_TYPE_BUFFER) {
dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n", dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
__func__, dimm_name, cmd_name, out_obj->type); __func__, dimm_name, cmd_name, out_obj->type);
@ -921,6 +962,30 @@ static ssize_t serial_show(struct device *dev,
} }
static DEVICE_ATTR_RO(serial); static DEVICE_ATTR_RO(serial);
static ssize_t family_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
if (nfit_mem->family < 0)
return -ENXIO;
return sprintf(buf, "%d\n", nfit_mem->family);
}
static DEVICE_ATTR_RO(family);
static ssize_t dsm_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
if (nfit_mem->family < 0)
return -ENXIO;
return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask);
}
static DEVICE_ATTR_RO(dsm_mask);
static ssize_t flags_show(struct device *dev, static ssize_t flags_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@ -946,6 +1011,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
&dev_attr_serial.attr, &dev_attr_serial.attr,
&dev_attr_rev_id.attr, &dev_attr_rev_id.attr,
&dev_attr_flags.attr, &dev_attr_flags.attr,
&dev_attr_family.attr,
&dev_attr_dsm_mask.attr,
NULL, NULL,
}; };
@ -992,10 +1059,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
{ {
struct acpi_device *adev, *adev_dimm; struct acpi_device *adev, *adev_dimm;
struct device *dev = acpi_desc->dev; struct device *dev = acpi_desc->dev;
const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM); unsigned long dsm_mask;
const u8 *uuid;
int i; int i;
nfit_mem->dsm_mask = acpi_desc->dimm_dsm_force_en; /* nfit test assumes 1:1 relationship between commands and dsms */
nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
nfit_mem->family = NVDIMM_FAMILY_INTEL;
adev = to_acpi_dev(acpi_desc); adev = to_acpi_dev(acpi_desc);
if (!adev) if (!adev)
return 0; return 0;
@ -1008,7 +1078,35 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
return force_enable_dimms ? 0 : -ENODEV; return force_enable_dimms ? 0 : -ENODEV;
} }
for (i = ND_CMD_SMART; i <= ND_CMD_VENDOR; i++) /*
* Until standardization materializes we need to consider up to 3
* different command sets. Note, that checking for function0 (bit0)
* tells us if any commands are reachable through this uuid.
*/
for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++)
if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
break;
/* limit the supported commands to those that are publicly documented */
nfit_mem->family = i;
if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
dsm_mask = 0x3fe;
if (disable_vendor_specific)
dsm_mask &= ~(1 << ND_CMD_VENDOR);
} else if (nfit_mem->family == NVDIMM_FAMILY_HPE1)
dsm_mask = 0x1c3c76;
else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) {
dsm_mask = 0x1fe;
if (disable_vendor_specific)
dsm_mask &= ~(1 << 8);
} else {
dev_err(dev, "unknown dimm command family\n");
nfit_mem->family = -1;
return force_enable_dimms ? 0 : -ENODEV;
}
uuid = to_nfit_uuid(nfit_mem->family);
for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i)) if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i))
set_bit(i, &nfit_mem->dsm_mask); set_bit(i, &nfit_mem->dsm_mask);
@ -1021,8 +1119,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
int dimm_count = 0; int dimm_count = 0;
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
unsigned long flags = 0, cmd_mask;
struct nvdimm *nvdimm; struct nvdimm *nvdimm;
unsigned long flags = 0;
u32 device_handle; u32 device_handle;
u16 mem_flags; u16 mem_flags;
int rc; int rc;
@ -1045,9 +1143,18 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
if (rc) if (rc)
continue; continue;
/*
* TODO: provide translation for non-NVDIMM_FAMILY_INTEL
* devices (i.e. from nd_cmd to acpi_dsm) to standardize the
* userspace interface.
*/
cmd_mask = 1UL << ND_CMD_CALL;
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
cmd_mask |= nfit_mem->dsm_mask;
nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
acpi_nfit_dimm_attribute_groups, acpi_nfit_dimm_attribute_groups,
flags, &nfit_mem->dsm_mask); flags, cmd_mask);
if (!nvdimm) if (!nvdimm)
return -ENOMEM; return -ENOMEM;
@ -1076,14 +1183,14 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
struct acpi_device *adev; struct acpi_device *adev;
int i; int i;
nd_desc->dsm_mask = acpi_desc->bus_dsm_force_en; nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
adev = to_acpi_dev(acpi_desc); adev = to_acpi_dev(acpi_desc);
if (!adev) if (!adev)
return; return;
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++) for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i)) if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i))
set_bit(i, &nd_desc->dsm_mask); set_bit(i, &nd_desc->cmd_mask);
} }
static ssize_t range_index_show(struct device *dev, static ssize_t range_index_show(struct device *dev,
@ -2532,6 +2639,8 @@ static __init int nfit_init(void)
acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]); acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]);
acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]); acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]);
acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]); acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]);
acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE1, nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE2, nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
nfit_wq = create_singlethread_workqueue("nfit"); nfit_wq = create_singlethread_workqueue("nfit");
if (!nfit_wq) if (!nfit_wq)

View File

@ -21,13 +21,25 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <acpi/acuuid.h> #include <acpi/acuuid.h>
/* ACPI 6.1 */
#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" #define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
/* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf */
#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */
#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6"
#define UUID_NFIT_DIMM_N_HPE2 "5008664b-b758-41a0-a03c-27c2f2d04f7e"
#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
| ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
| ACPI_NFIT_MEM_NOT_ARMED) | ACPI_NFIT_MEM_NOT_ARMED)
enum nfit_uuids { enum nfit_uuids {
/* for simplicity alias the uuid index with the family id */
NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL,
NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1,
NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
NFIT_SPA_VOLATILE, NFIT_SPA_VOLATILE,
NFIT_SPA_PM, NFIT_SPA_PM,
NFIT_SPA_DCR, NFIT_SPA_DCR,
@ -37,7 +49,6 @@ enum nfit_uuids {
NFIT_SPA_PDISK, NFIT_SPA_PDISK,
NFIT_SPA_PCD, NFIT_SPA_PCD,
NFIT_DEV_BUS, NFIT_DEV_BUS,
NFIT_DEV_DIMM,
NFIT_UUID_MAX, NFIT_UUID_MAX,
}; };
@ -111,6 +122,7 @@ struct nfit_mem {
struct acpi_device *adev; struct acpi_device *adev;
struct acpi_nfit_desc *acpi_desc; struct acpi_nfit_desc *acpi_desc;
unsigned long dsm_mask; unsigned long dsm_mask;
int family;
}; };
struct acpi_nfit_desc { struct acpi_nfit_desc {
@ -133,8 +145,8 @@ struct acpi_nfit_desc {
size_t ars_status_size; size_t ars_status_size;
struct work_struct work; struct work_struct work;
unsigned int cancel:1; unsigned int cancel:1;
unsigned long dimm_dsm_force_en; unsigned long dimm_cmd_force_en;
unsigned long bus_dsm_force_en; unsigned long bus_cmd_force_en;
int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
void *iobuf, u64 len, int rw); void *iobuf, u64 len, int rw);
}; };

View File

@ -625,7 +625,7 @@ acpi_status acpi_evaluate_lck(acpi_handle handle, int lock)
* some old BIOSes do expect a buffer or an integer etc. * some old BIOSes do expect a buffer or an integer etc.
*/ */
union acpi_object * union acpi_object *
acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, int rev, int func, acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 func,
union acpi_object *argv4) union acpi_object *argv4)
{ {
acpi_status ret; acpi_status ret;
@ -674,7 +674,7 @@ EXPORT_SYMBOL(acpi_evaluate_dsm);
* functions. Currently only support 64 functions at maximum, should be * functions. Currently only support 64 functions at maximum, should be
* enough for now. * enough for now.
*/ */
bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs)
{ {
int i; int i;
u64 mask = 0; u64 mask = 0;

View File

@ -443,6 +443,12 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
.out_num = 3, .out_num = 3,
.out_sizes = { 4, 4, UINT_MAX, }, .out_sizes = { 4, 4, UINT_MAX, },
}, },
[ND_CMD_CALL] = {
.in_num = 2,
.in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
.out_num = 1,
.out_sizes = { UINT_MAX, },
},
}; };
const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd)
@ -477,6 +483,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
.out_num = 3, .out_num = 3,
.out_sizes = { 4, 4, 8, }, .out_sizes = { 4, 4, 8, },
}, },
[ND_CMD_CALL] = {
.in_num = 2,
.in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
.out_num = 1,
.out_sizes = { UINT_MAX, },
},
}; };
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
@ -504,6 +516,10 @@ u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
struct nd_cmd_vendor_hdr *hdr = buf; struct nd_cmd_vendor_hdr *hdr = buf;
return hdr->in_length; return hdr->in_length;
} else if (cmd == ND_CMD_CALL) {
struct nd_cmd_pkg *pkg = buf;
return pkg->nd_size_in;
} }
return UINT_MAX; return UINT_MAX;
@ -526,6 +542,12 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
return out_field[1]; return out_field[1];
else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
return out_field[1] - 8; return out_field[1] - 8;
else if (cmd == ND_CMD_CALL) {
struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field;
return pkg->nd_size_out;
}
return UINT_MAX; return UINT_MAX;
} }
@ -592,25 +614,31 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
unsigned int cmd = _IOC_NR(ioctl_cmd); unsigned int cmd = _IOC_NR(ioctl_cmd);
void __user *p = (void __user *) arg; void __user *p = (void __user *) arg;
struct device *dev = &nvdimm_bus->dev; struct device *dev = &nvdimm_bus->dev;
struct nd_cmd_pkg pkg;
const char *cmd_name, *dimm_name; const char *cmd_name, *dimm_name;
unsigned long dsm_mask; unsigned long cmd_mask;
void *buf; void *buf;
int rc, i; int rc, i;
if (nvdimm) { if (nvdimm) {
desc = nd_cmd_dimm_desc(cmd); desc = nd_cmd_dimm_desc(cmd);
cmd_name = nvdimm_cmd_name(cmd); cmd_name = nvdimm_cmd_name(cmd);
dsm_mask = nvdimm->dsm_mask ? *(nvdimm->dsm_mask) : 0; cmd_mask = nvdimm->cmd_mask;
dimm_name = dev_name(&nvdimm->dev); dimm_name = dev_name(&nvdimm->dev);
} else { } else {
desc = nd_cmd_bus_desc(cmd); desc = nd_cmd_bus_desc(cmd);
cmd_name = nvdimm_bus_cmd_name(cmd); cmd_name = nvdimm_bus_cmd_name(cmd);
dsm_mask = nd_desc->dsm_mask; cmd_mask = nd_desc->cmd_mask;
dimm_name = "bus"; dimm_name = "bus";
} }
if (cmd == ND_CMD_CALL) {
if (copy_from_user(&pkg, p, sizeof(pkg)))
return -EFAULT;
}
if (!desc || (desc->out_num + desc->in_num == 0) || if (!desc || (desc->out_num + desc->in_num == 0) ||
!test_bit(cmd, &dsm_mask)) !test_bit(cmd, &cmd_mask))
return -ENOTTY; return -ENOTTY;
/* fail write commands (when read-only) */ /* fail write commands (when read-only) */
@ -620,6 +648,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
case ND_CMD_SET_CONFIG_DATA: case ND_CMD_SET_CONFIG_DATA:
case ND_CMD_ARS_START: case ND_CMD_ARS_START:
case ND_CMD_CLEAR_ERROR: case ND_CMD_CLEAR_ERROR:
case ND_CMD_CALL:
dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
nvdimm ? nvdimm_cmd_name(cmd) nvdimm ? nvdimm_cmd_name(cmd)
: nvdimm_bus_cmd_name(cmd)); : nvdimm_bus_cmd_name(cmd));
@ -647,6 +676,16 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
in_len += in_size; in_len += in_size;
} }
if (cmd == ND_CMD_CALL) {
dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
__func__, dimm_name, pkg.nd_command,
in_len, out_len, buf_len);
for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++)
if (pkg.nd_reserved2[i])
return -EINVAL;
}
/* process an output envelope */ /* process an output envelope */
for (i = 0; i < desc->out_num; i++) { for (i = 0; i < desc->out_num; i++) {
u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,

View File

@ -251,7 +251,7 @@ static ssize_t commands_show(struct device *dev,
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
for_each_set_bit(cmd, &nd_desc->dsm_mask, BITS_PER_LONG) for_each_set_bit(cmd, &nd_desc->cmd_mask, BITS_PER_LONG)
len += sprintf(buf + len, "%s ", nvdimm_bus_cmd_name(cmd)); len += sprintf(buf + len, "%s ", nvdimm_bus_cmd_name(cmd));
len += sprintf(buf + len, "\n"); len += sprintf(buf + len, "\n");
return len; return len;

View File

@ -37,9 +37,9 @@ static int __validate_dimm(struct nvdimm_drvdata *ndd)
nvdimm = to_nvdimm(ndd->dev); nvdimm = to_nvdimm(ndd->dev);
if (!nvdimm->dsm_mask) if (!nvdimm->cmd_mask)
return -ENXIO; return -ENXIO;
if (!test_bit(ND_CMD_GET_CONFIG_DATA, nvdimm->dsm_mask)) if (!test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask))
return -ENXIO; return -ENXIO;
return 0; return 0;
@ -263,6 +263,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm)
} }
EXPORT_SYMBOL_GPL(nvdimm_name); EXPORT_SYMBOL_GPL(nvdimm_name);
unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
{
return nvdimm->cmd_mask;
}
EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
void *nvdimm_provider_data(struct nvdimm *nvdimm) void *nvdimm_provider_data(struct nvdimm *nvdimm)
{ {
if (nvdimm) if (nvdimm)
@ -277,10 +283,10 @@ static ssize_t commands_show(struct device *dev,
struct nvdimm *nvdimm = to_nvdimm(dev); struct nvdimm *nvdimm = to_nvdimm(dev);
int cmd, len = 0; int cmd, len = 0;
if (!nvdimm->dsm_mask) if (!nvdimm->cmd_mask)
return sprintf(buf, "\n"); return sprintf(buf, "\n");
for_each_set_bit(cmd, nvdimm->dsm_mask, BITS_PER_LONG) for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
len += sprintf(buf + len, "\n"); len += sprintf(buf + len, "\n");
return len; return len;
@ -340,7 +346,7 @@ EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
const struct attribute_group **groups, unsigned long flags, const struct attribute_group **groups, unsigned long flags,
unsigned long *dsm_mask) unsigned long cmd_mask)
{ {
struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
struct device *dev; struct device *dev;
@ -355,7 +361,7 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
} }
nvdimm->provider_data = provider_data; nvdimm->provider_data = provider_data;
nvdimm->flags = flags; nvdimm->flags = flags;
nvdimm->dsm_mask = dsm_mask; nvdimm->cmd_mask = cmd_mask;
atomic_set(&nvdimm->busy, 0); atomic_set(&nvdimm->busy, 0);
dev = &nvdimm->dev; dev = &nvdimm->dev;
dev_set_name(dev, "nmem%d", nvdimm->id); dev_set_name(dev, "nmem%d", nvdimm->id);

View File

@ -37,7 +37,7 @@ struct nvdimm_bus {
struct nvdimm { struct nvdimm {
unsigned long flags; unsigned long flags;
void *provider_data; void *provider_data;
unsigned long *dsm_mask; unsigned long cmd_mask;
struct device dev; struct device dev;
atomic_t busy; atomic_t busy;
int id; int id;

View File

@ -61,12 +61,12 @@ bool acpi_ata_match(acpi_handle handle);
bool acpi_bay_match(acpi_handle handle); bool acpi_bay_match(acpi_handle handle);
bool acpi_dock_match(acpi_handle handle); bool acpi_dock_match(acpi_handle handle);
bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs); bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs);
union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid,
int rev, int func, union acpi_object *argv4); u64 rev, u64 func, union acpi_object *argv4);
static inline union acpi_object * static inline union acpi_object *
acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, int rev, int func, acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, u64 rev, u64 func,
union acpi_object *argv4, acpi_object_type type) union acpi_object *argv4, acpi_object_type type)
{ {
union acpi_object *obj; union acpi_object *obj;

View File

@ -27,7 +27,7 @@ enum {
/* need to set a limit somewhere, but yes, this is likely overkill */ /* need to set a limit somewhere, but yes, this is likely overkill */
ND_IOCTL_MAX_BUFLEN = SZ_4M, ND_IOCTL_MAX_BUFLEN = SZ_4M,
ND_CMD_MAX_ELEM = 5, ND_CMD_MAX_ELEM = 5,
ND_CMD_MAX_ENVELOPE = 16, ND_CMD_MAX_ENVELOPE = 256,
ND_MAX_MAPPINGS = 32, ND_MAX_MAPPINGS = 32,
/* region flag indicating to direct-map persistent memory by default */ /* region flag indicating to direct-map persistent memory by default */
@ -68,7 +68,7 @@ struct nd_mapping {
struct nvdimm_bus_descriptor { struct nvdimm_bus_descriptor {
const struct attribute_group **attr_groups; const struct attribute_group **attr_groups;
unsigned long dsm_mask; unsigned long cmd_mask;
char *provider_name; char *provider_name;
ndctl_fn ndctl; ndctl_fn ndctl;
int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
@ -130,10 +130,11 @@ struct nd_region *to_nd_region(struct device *dev);
struct nd_blk_region *to_nd_blk_region(struct device *dev); struct nd_blk_region *to_nd_blk_region(struct device *dev);
struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus); struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
const char *nvdimm_name(struct nvdimm *nvdimm); const char *nvdimm_name(struct nvdimm *nvdimm);
unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
void *nvdimm_provider_data(struct nvdimm *nvdimm); void *nvdimm_provider_data(struct nvdimm *nvdimm);
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
const struct attribute_group **groups, unsigned long flags, const struct attribute_group **groups, unsigned long flags,
unsigned long *dsm_mask); unsigned long cmd_mask);
const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,

View File

@ -159,6 +159,7 @@ enum {
ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7, ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7,
ND_CMD_VENDOR_EFFECT_LOG = 8, ND_CMD_VENDOR_EFFECT_LOG = 8,
ND_CMD_VENDOR = 9, ND_CMD_VENDOR = 9,
ND_CMD_CALL = 10,
}; };
enum { enum {
@ -192,6 +193,7 @@ static inline const char *nvdimm_cmd_name(unsigned cmd)
[ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size", [ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size",
[ND_CMD_VENDOR_EFFECT_LOG] = "effect_log", [ND_CMD_VENDOR_EFFECT_LOG] = "effect_log",
[ND_CMD_VENDOR] = "vendor", [ND_CMD_VENDOR] = "vendor",
[ND_CMD_CALL] = "cmd_call",
}; };
if (cmd < ARRAY_SIZE(names) && names[cmd]) if (cmd < ARRAY_SIZE(names) && names[cmd])
@ -260,4 +262,44 @@ enum ars_masks {
ARS_STATUS_MASK = 0x0000FFFF, ARS_STATUS_MASK = 0x0000FFFF,
ARS_EXT_STATUS_SHIFT = 16, ARS_EXT_STATUS_SHIFT = 16,
}; };
/*
* struct nd_cmd_pkg
*
* is a wrapper to a quasi pass thru interface for invoking firmware
* associated with nvdimms.
*
* INPUT PARAMETERS
*
* nd_family corresponds to the firmware (e.g. DSM) interface.
*
* nd_command are the function index advertised by the firmware.
*
* nd_size_in is the size of the input parameters being passed to firmware
*
* OUTPUT PARAMETERS
*
* nd_fw_size is the size of the data firmware wants to return for
* the call. If nd_fw_size is greater than size of nd_size_out, only
* the first nd_size_out bytes are returned.
*/
struct nd_cmd_pkg {
__u64 nd_family; /* family of commands */
__u64 nd_command;
__u32 nd_size_in; /* INPUT: size of input args */
__u32 nd_size_out; /* INPUT: size of payload */
__u32 nd_reserved2[9]; /* reserved must be zero */
__u32 nd_fw_size; /* OUTPUT: size fw wants to return */
unsigned char nd_payload[]; /* Contents of call */
};
/* These NVDIMM families represent pre-standardization command sets */
#define NVDIMM_FAMILY_INTEL 0
#define NVDIMM_FAMILY_HPE1 1
#define NVDIMM_FAMILY_HPE2 2
#define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\
struct nd_cmd_pkg)
#endif /* __NDCTL_H__ */ #endif /* __NDCTL_H__ */

View File

@ -372,6 +372,7 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
{ {
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
unsigned int func = cmd;
int i, rc = 0, __cmd_rc; int i, rc = 0, __cmd_rc;
if (!cmd_rc) if (!cmd_rc)
@ -380,8 +381,23 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
if (nvdimm) { if (nvdimm) {
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask)) if (!nfit_mem)
return -ENOTTY;
if (cmd == ND_CMD_CALL) {
struct nd_cmd_pkg *call_pkg = buf;
buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
buf = (void *) call_pkg->nd_payload;
func = call_pkg->nd_command;
if (call_pkg->nd_family != nfit_mem->family)
return -ENOTTY;
}
if (!test_bit(cmd, &cmd_mask)
|| !test_bit(func, &nfit_mem->dsm_mask))
return -ENOTTY; return -ENOTTY;
/* lookup label space for the given dimm */ /* lookup label space for the given dimm */
@ -392,7 +408,7 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
if (i >= ARRAY_SIZE(handle)) if (i >= ARRAY_SIZE(handle))
return -ENXIO; return -ENXIO;
switch (cmd) { switch (func) {
case ND_CMD_GET_CONFIG_SIZE: case ND_CMD_GET_CONFIG_SIZE:
rc = nfit_test_cmd_get_config_size(buf, buf_len); rc = nfit_test_cmd_get_config_size(buf, buf_len);
break; break;
@ -416,10 +432,10 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
} else { } else {
struct ars_state *ars_state = &t->ars_state; struct ars_state *ars_state = &t->ars_state;
if (!nd_desc || !test_bit(cmd, &nd_desc->dsm_mask)) if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
return -ENOTTY; return -ENOTTY;
switch (cmd) { switch (func) {
case ND_CMD_ARS_CAP: case ND_CMD_ARS_CAP:
rc = nfit_test_cmd_ars_cap(buf, buf_len); rc = nfit_test_cmd_ars_cap(buf, buf_len);
break; break;
@ -1293,15 +1309,15 @@ static void nfit_test0_setup(struct nfit_test *t)
post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE); post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE);
acpi_desc = &t->acpi_desc; acpi_desc = &t->acpi_desc;
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
set_bit(ND_CMD_SMART, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_SMART, &acpi_desc->dimm_cmd_force_en);
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
} }
static void nfit_test1_setup(struct nfit_test *t) static void nfit_test1_setup(struct nfit_test *t)
@ -1359,10 +1375,10 @@ static void nfit_test1_setup(struct nfit_test *t)
post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE); post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE);
acpi_desc = &t->acpi_desc; acpi_desc = &t->acpi_desc;
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
} }
static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,