Merge branch 'for-4.7/dsm' into libnvdimm-for-next
This commit is contained in:
commit
1f716d05f8
|
@ -45,6 +45,11 @@ module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR);
|
|||
MODULE_PARM_DESC(scrub_overflow_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;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
const struct nd_cmd_desc *desc = NULL;
|
||||
struct device *dev = acpi_desc->dev;
|
||||
struct nd_cmd_pkg *call_pkg = NULL;
|
||||
const char *cmd_name, *dimm_name;
|
||||
unsigned long dsm_mask;
|
||||
unsigned long cmd_mask, dsm_mask;
|
||||
acpi_handle handle;
|
||||
unsigned int func;
|
||||
const u8 *uuid;
|
||||
u32 offset;
|
||||
int rc, i;
|
||||
|
||||
func = cmd;
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
call_pkg = buf;
|
||||
func = call_pkg->nd_command;
|
||||
}
|
||||
|
||||
if (nvdimm) {
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_device *adev = nfit_mem->adev;
|
||||
|
||||
if (!adev)
|
||||
return -ENOTTY;
|
||||
if (call_pkg && nfit_mem->family != call_pkg->nd_family)
|
||||
return -ENOTTY;
|
||||
|
||||
dimm_name = nvdimm_name(nvdimm);
|
||||
cmd_name = nvdimm_cmd_name(cmd);
|
||||
cmd_mask = nvdimm_cmd_mask(nvdimm);
|
||||
dsm_mask = nfit_mem->dsm_mask;
|
||||
desc = nd_cmd_dimm_desc(cmd);
|
||||
uuid = to_nfit_uuid(NFIT_DEV_DIMM);
|
||||
uuid = to_nfit_uuid(nfit_mem->family);
|
||||
handle = adev->handle;
|
||||
} else {
|
||||
struct acpi_device *adev = to_acpi_dev(acpi_desc);
|
||||
|
||||
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);
|
||||
uuid = to_nfit_uuid(NFIT_DEV_BUS);
|
||||
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)))
|
||||
return -ENOTTY;
|
||||
|
||||
if (!test_bit(cmd, &dsm_mask))
|
||||
if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask))
|
||||
return -ENOTTY;
|
||||
|
||||
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,
|
||||
i, buf);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
|
||||
dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__,
|
||||
dimm_name, cmd_name, in_buf.buffer.length);
|
||||
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4,
|
||||
4, in_buf.buffer.pointer, min_t(u32, 128,
|
||||
in_buf.buffer.length), true);
|
||||
if (call_pkg) {
|
||||
/* skip over package wrapper */
|
||||
in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
|
||||
in_buf.buffer.length = call_pkg->nd_size_in;
|
||||
}
|
||||
|
||||
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) {
|
||||
dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
|
||||
cmd_name);
|
||||
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) {
|
||||
dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
|
||||
__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 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,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -946,6 +1011,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
|
|||
&dev_attr_serial.attr,
|
||||
&dev_attr_rev_id.attr,
|
||||
&dev_attr_flags.attr,
|
||||
&dev_attr_family.attr,
|
||||
&dev_attr_dsm_mask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -992,10 +1059,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|||
{
|
||||
struct acpi_device *adev, *adev_dimm;
|
||||
struct device *dev = acpi_desc->dev;
|
||||
const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM);
|
||||
unsigned long dsm_mask;
|
||||
const u8 *uuid;
|
||||
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);
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
@ -1008,7 +1078,35 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|||
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))
|
||||
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;
|
||||
|
||||
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
|
||||
unsigned long flags = 0, cmd_mask;
|
||||
struct nvdimm *nvdimm;
|
||||
unsigned long flags = 0;
|
||||
u32 device_handle;
|
||||
u16 mem_flags;
|
||||
int rc;
|
||||
|
@ -1045,9 +1143,18 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
|
|||
if (rc)
|
||||
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,
|
||||
acpi_nfit_dimm_attribute_groups,
|
||||
flags, &nfit_mem->dsm_mask);
|
||||
flags, cmd_mask);
|
||||
if (!nvdimm)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1076,14 +1183,14 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
|
|||
struct acpi_device *adev;
|
||||
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);
|
||||
if (!adev)
|
||||
return;
|
||||
|
||||
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; 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,
|
||||
|
@ -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_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_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");
|
||||
if (!nfit_wq)
|
||||
|
|
|
@ -21,13 +21,25 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <acpi/acuuid.h>
|
||||
|
||||
/* ACPI 6.1 */
|
||||
#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"
|
||||
|
||||
/* 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 \
|
||||
| ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
|
||||
| ACPI_NFIT_MEM_NOT_ARMED)
|
||||
|
||||
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_PM,
|
||||
NFIT_SPA_DCR,
|
||||
|
@ -37,7 +49,6 @@ enum nfit_uuids {
|
|||
NFIT_SPA_PDISK,
|
||||
NFIT_SPA_PCD,
|
||||
NFIT_DEV_BUS,
|
||||
NFIT_DEV_DIMM,
|
||||
NFIT_UUID_MAX,
|
||||
};
|
||||
|
||||
|
@ -111,6 +122,7 @@ struct nfit_mem {
|
|||
struct acpi_device *adev;
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
unsigned long dsm_mask;
|
||||
int family;
|
||||
};
|
||||
|
||||
struct acpi_nfit_desc {
|
||||
|
@ -133,8 +145,8 @@ struct acpi_nfit_desc {
|
|||
size_t ars_status_size;
|
||||
struct work_struct work;
|
||||
unsigned int cancel:1;
|
||||
unsigned long dimm_dsm_force_en;
|
||||
unsigned long bus_dsm_force_en;
|
||||
unsigned long dimm_cmd_force_en;
|
||||
unsigned long bus_cmd_force_en;
|
||||
int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
|
||||
void *iobuf, u64 len, int rw);
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
acpi_status ret;
|
||||
|
@ -674,7 +674,7 @@ EXPORT_SYMBOL(acpi_evaluate_dsm);
|
|||
* functions. Currently only support 64 functions at maximum, should be
|
||||
* 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;
|
||||
u64 mask = 0;
|
||||
|
|
|
@ -443,6 +443,12 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
|
|||
.out_num = 3,
|
||||
.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)
|
||||
|
@ -477,6 +483,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
|
|||
.out_num = 3,
|
||||
.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)
|
||||
|
@ -504,6 +516,10 @@ u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
|
|||
struct nd_cmd_vendor_hdr *hdr = buf;
|
||||
|
||||
return hdr->in_length;
|
||||
} else if (cmd == ND_CMD_CALL) {
|
||||
struct nd_cmd_pkg *pkg = buf;
|
||||
|
||||
return pkg->nd_size_in;
|
||||
}
|
||||
|
||||
return UINT_MAX;
|
||||
|
@ -526,6 +542,12 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
|
|||
return out_field[1];
|
||||
else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
|
||||
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;
|
||||
}
|
||||
|
@ -592,25 +614,31 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
|||
unsigned int cmd = _IOC_NR(ioctl_cmd);
|
||||
void __user *p = (void __user *) arg;
|
||||
struct device *dev = &nvdimm_bus->dev;
|
||||
struct nd_cmd_pkg pkg;
|
||||
const char *cmd_name, *dimm_name;
|
||||
unsigned long dsm_mask;
|
||||
unsigned long cmd_mask;
|
||||
void *buf;
|
||||
int rc, i;
|
||||
|
||||
if (nvdimm) {
|
||||
desc = nd_cmd_dimm_desc(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);
|
||||
} else {
|
||||
desc = nd_cmd_bus_desc(cmd);
|
||||
cmd_name = nvdimm_bus_cmd_name(cmd);
|
||||
dsm_mask = nd_desc->dsm_mask;
|
||||
cmd_mask = nd_desc->cmd_mask;
|
||||
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) ||
|
||||
!test_bit(cmd, &dsm_mask))
|
||||
!test_bit(cmd, &cmd_mask))
|
||||
return -ENOTTY;
|
||||
|
||||
/* 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_ARS_START:
|
||||
case ND_CMD_CLEAR_ERROR:
|
||||
case ND_CMD_CALL:
|
||||
dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
|
||||
nvdimm ? nvdimm_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;
|
||||
}
|
||||
|
||||
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 */
|
||||
for (i = 0; i < desc->out_num; i++) {
|
||||
u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,
|
||||
|
|
|
@ -251,7 +251,7 @@ static ssize_t commands_show(struct device *dev,
|
|||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
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, "\n");
|
||||
return len;
|
||||
|
|
|
@ -37,9 +37,9 @@ static int __validate_dimm(struct nvdimm_drvdata *ndd)
|
|||
|
||||
nvdimm = to_nvdimm(ndd->dev);
|
||||
|
||||
if (!nvdimm->dsm_mask)
|
||||
if (!nvdimm->cmd_mask)
|
||||
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 0;
|
||||
|
@ -263,6 +263,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm)
|
|||
}
|
||||
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)
|
||||
{
|
||||
if (nvdimm)
|
||||
|
@ -277,10 +283,10 @@ static ssize_t commands_show(struct device *dev,
|
|||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
int cmd, len = 0;
|
||||
|
||||
if (!nvdimm->dsm_mask)
|
||||
if (!nvdimm->cmd_mask)
|
||||
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, "\n");
|
||||
return len;
|
||||
|
@ -340,7 +346,7 @@ EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
|
|||
|
||||
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
|
||||
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 device *dev;
|
||||
|
@ -355,7 +361,7 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
|
|||
}
|
||||
nvdimm->provider_data = provider_data;
|
||||
nvdimm->flags = flags;
|
||||
nvdimm->dsm_mask = dsm_mask;
|
||||
nvdimm->cmd_mask = cmd_mask;
|
||||
atomic_set(&nvdimm->busy, 0);
|
||||
dev = &nvdimm->dev;
|
||||
dev_set_name(dev, "nmem%d", nvdimm->id);
|
||||
|
|
|
@ -37,7 +37,7 @@ struct nvdimm_bus {
|
|||
struct nvdimm {
|
||||
unsigned long flags;
|
||||
void *provider_data;
|
||||
unsigned long *dsm_mask;
|
||||
unsigned long cmd_mask;
|
||||
struct device dev;
|
||||
atomic_t busy;
|
||||
int id;
|
||||
|
|
|
@ -61,12 +61,12 @@ bool acpi_ata_match(acpi_handle handle);
|
|||
bool acpi_bay_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,
|
||||
int rev, int func, union acpi_object *argv4);
|
||||
u64 rev, u64 func, union acpi_object *argv4);
|
||||
|
||||
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 *obj;
|
||||
|
|
|
@ -27,7 +27,7 @@ enum {
|
|||
/* need to set a limit somewhere, but yes, this is likely overkill */
|
||||
ND_IOCTL_MAX_BUFLEN = SZ_4M,
|
||||
ND_CMD_MAX_ELEM = 5,
|
||||
ND_CMD_MAX_ENVELOPE = 16,
|
||||
ND_CMD_MAX_ENVELOPE = 256,
|
||||
ND_MAX_MAPPINGS = 32,
|
||||
|
||||
/* region flag indicating to direct-map persistent memory by default */
|
||||
|
@ -68,7 +68,7 @@ struct nd_mapping {
|
|||
|
||||
struct nvdimm_bus_descriptor {
|
||||
const struct attribute_group **attr_groups;
|
||||
unsigned long dsm_mask;
|
||||
unsigned long cmd_mask;
|
||||
char *provider_name;
|
||||
ndctl_fn ndctl;
|
||||
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 nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
|
||||
const char *nvdimm_name(struct nvdimm *nvdimm);
|
||||
unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
|
||||
void *nvdimm_provider_data(struct nvdimm *nvdimm);
|
||||
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
|
||||
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_bus_desc(int cmd);
|
||||
u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
|
||||
|
|
|
@ -159,6 +159,7 @@ enum {
|
|||
ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7,
|
||||
ND_CMD_VENDOR_EFFECT_LOG = 8,
|
||||
ND_CMD_VENDOR = 9,
|
||||
ND_CMD_CALL = 10,
|
||||
};
|
||||
|
||||
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] = "effect_log",
|
||||
[ND_CMD_VENDOR] = "vendor",
|
||||
[ND_CMD_CALL] = "cmd_call",
|
||||
};
|
||||
|
||||
if (cmd < ARRAY_SIZE(names) && names[cmd])
|
||||
|
@ -260,4 +262,44 @@ enum ars_masks {
|
|||
ARS_STATUS_MASK = 0x0000FFFF,
|
||||
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__ */
|
||||
|
|
|
@ -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 nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
|
||||
unsigned int func = cmd;
|
||||
int i, rc = 0, __cmd_rc;
|
||||
|
||||
if (!cmd_rc)
|
||||
|
@ -380,8 +381,23 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
|
||||
if (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;
|
||||
|
||||
/* 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))
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
switch (func) {
|
||||
case ND_CMD_GET_CONFIG_SIZE:
|
||||
rc = nfit_test_cmd_get_config_size(buf, buf_len);
|
||||
break;
|
||||
|
@ -416,10 +432,10 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
} else {
|
||||
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;
|
||||
|
||||
switch (cmd) {
|
||||
switch (func) {
|
||||
case ND_CMD_ARS_CAP:
|
||||
rc = nfit_test_cmd_ars_cap(buf, buf_len);
|
||||
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);
|
||||
|
||||
acpi_desc = &t->acpi_desc;
|
||||
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
|
||||
set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
|
||||
set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
|
||||
set_bit(ND_CMD_SMART, &acpi_desc->dimm_dsm_force_en);
|
||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_SMART_THRESHOLD, &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_cmd_force_en);
|
||||
set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_SMART, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
acpi_desc = &t->acpi_desc;
|
||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
||||
set_bit(ND_CMD_CLEAR_ERROR, &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_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_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,
|
||||
|
|
Loading…
Reference in New Issue