You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'libnvdimm-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
"The bulk of this update was stabilized before the merge window and
appeared in -next. The "device dax" implementation was revised this
week in response to review feedback, and to address failures detected
by the recently expanded ndctl unit test suite.
Not included in this pull request are two dax topic branches (dax
error handling, and dax radix-tree locking). These topics were
deferred to get a few more days of -next integration testing, and to
coordinate a branch baseline with Ted and the ext4 tree. Vishal and
Ross will send the error handling and locking topics respectively in
the next few days.
This branch has received a positive build result from the kbuild robot
across 226 configs.
Summary:
- Device DAX for persistent memory: Device DAX is the device-centric
analogue of Filesystem DAX (CONFIG_FS_DAX). It allows memory
ranges to be allocated and mapped without need of an intervening
file system. Device DAX is strict, precise and predictable.
Specifically this interface:
a) Guarantees fault granularity with respect to a given page size
(pte, pmd, or pud) set at configuration time.
b) Enforces deterministic behavior by being strict about what
fault scenarios are supported.
Persistent memory is the first target, but the mechanism is also
targeted for exclusive allocations of performance/feature
differentiated memory ranges.
- Support for the HPE DSM (device specific method) command formats.
This enables management of these first generation devices until a
unified DSM specification materializes.
- Further ACPI 6.1 compliance with support for the common dimm
identifier format.
- Various fixes and cleanups across the subsystem"
* tag 'libnvdimm-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (40 commits)
libnvdimm, dax: fix deletion
libnvdimm, dax: fix alignment validation
libnvdimm, dax: autodetect support
libnvdimm: release ida resources
Revert "block: enable dax for raw block devices"
/dev/dax, core: file operations and dax-mmap
/dev/dax, pmem: direct access to persistent memory
libnvdimm: stop requiring a driver ->remove() method
libnvdimm, dax: record the specified alignment of a dax-device instance
libnvdimm, dax: reserve space to store labels for device-dax
libnvdimm, dax: introduce device-dax infrastructure
nfit: add sysfs dimm 'family' and 'dsm_mask' attributes
tools/testing/nvdimm: ND_CMD_CALL support
nfit: disable vendor specific commands
nfit: export subsystem ids as attributes
nfit: fix format interface code byte order per ACPI6.1
nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism
nfit, libnvdimm: clarify "commands" vs "_DSMs"
libnvdimm: increase max envelope size for ioctl
acpi/nfit: Add sysfs "id" for NVDIMM ID
...
This commit is contained in:
@@ -407,35 +407,6 @@ static inline int is_unrecognized_ioctl(int ret)
|
||||
ret == -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_DAX
|
||||
bool blkdev_dax_capable(struct block_device *bdev)
|
||||
{
|
||||
struct gendisk *disk = bdev->bd_disk;
|
||||
|
||||
if (!disk->fops->direct_access)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the partition is not aligned on a page boundary, we can't
|
||||
* do dax I/O to it.
|
||||
*/
|
||||
if ((bdev->bd_part->start_sect % (PAGE_SIZE / 512))
|
||||
|| (bdev->bd_part->nr_sects % (PAGE_SIZE / 512)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the device has known bad blocks, force all I/O through the
|
||||
* driver / page cache.
|
||||
*
|
||||
* TODO: support finer grained dax error handling
|
||||
*/
|
||||
if (disk->bb && disk->bb->count)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
|
||||
unsigned cmd, unsigned long arg)
|
||||
{
|
||||
@@ -598,9 +569,6 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
||||
case BLKTRACESETUP:
|
||||
case BLKTRACETEARDOWN:
|
||||
return blk_trace_ioctl(bdev, cmd, argp);
|
||||
case BLKDAXGET:
|
||||
return put_int(arg, !!(bdev->bd_inode->i_flags & S_DAX));
|
||||
break;
|
||||
case IOC_PR_REGISTER:
|
||||
return blkdev_pr_register(bdev, argp);
|
||||
case IOC_PR_RESERVE:
|
||||
|
||||
@@ -192,6 +192,8 @@ source "drivers/android/Kconfig"
|
||||
|
||||
source "drivers/nvdimm/Kconfig"
|
||||
|
||||
source "drivers/dax/Kconfig"
|
||||
|
||||
source "drivers/nvmem/Kconfig"
|
||||
|
||||
source "drivers/hwtracing/stm/Kconfig"
|
||||
|
||||
@@ -66,6 +66,7 @@ obj-$(CONFIG_PARPORT) += parport/
|
||||
obj-$(CONFIG_NVM) += lightnvm/
|
||||
obj-y += base/ block/ misc/ mfd/ nfc/
|
||||
obj-$(CONFIG_LIBNVDIMM) += nvdimm/
|
||||
obj-$(CONFIG_DEV_DAX) += dax/
|
||||
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
|
||||
obj-$(CONFIG_NUBUS) += nubus/
|
||||
obj-y += macintosh/
|
||||
|
||||
+256
-30
@@ -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);
|
||||
@@ -658,6 +699,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
|
||||
if (!nfit_mem)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(&nfit_mem->list);
|
||||
nfit_mem->acpi_desc = acpi_desc;
|
||||
list_add(&nfit_mem->list, &acpi_desc->dimms);
|
||||
}
|
||||
|
||||
@@ -819,7 +861,7 @@ static ssize_t vendor_show(struct device *dev,
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", dcr->vendor_id);
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(vendor);
|
||||
|
||||
@@ -828,7 +870,7 @@ static ssize_t rev_id_show(struct device *dev,
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", dcr->revision_id);
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(rev_id);
|
||||
|
||||
@@ -837,28 +879,142 @@ static ssize_t device_show(struct device *dev,
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", dcr->device_id);
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->device_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(device);
|
||||
|
||||
static ssize_t subsystem_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(subsystem_vendor);
|
||||
|
||||
static ssize_t subsystem_rev_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "0x%04x\n",
|
||||
be16_to_cpu(dcr->subsystem_revision_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(subsystem_rev_id);
|
||||
|
||||
static ssize_t subsystem_device_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id));
|
||||
}
|
||||
static DEVICE_ATTR_RO(subsystem_device);
|
||||
|
||||
static int num_nvdimm_formats(struct nvdimm *nvdimm)
|
||||
{
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
int formats = 0;
|
||||
|
||||
if (nfit_mem->memdev_pmem)
|
||||
formats++;
|
||||
if (nfit_mem->memdev_bdw)
|
||||
formats++;
|
||||
return formats;
|
||||
}
|
||||
|
||||
static ssize_t format_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", dcr->code);
|
||||
return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->code));
|
||||
}
|
||||
static DEVICE_ATTR_RO(format);
|
||||
|
||||
static ssize_t format1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u32 handle;
|
||||
ssize_t rc = -ENXIO;
|
||||
struct nfit_mem *nfit_mem;
|
||||
struct nfit_memdev *nfit_memdev;
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
acpi_desc = nfit_mem->acpi_desc;
|
||||
handle = to_nfit_memdev(dev)->device_handle;
|
||||
|
||||
/* assumes DIMMs have at most 2 published interface codes */
|
||||
mutex_lock(&acpi_desc->init_mutex);
|
||||
list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
|
||||
struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
|
||||
struct nfit_dcr *nfit_dcr;
|
||||
|
||||
if (memdev->device_handle != handle)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
|
||||
if (nfit_dcr->dcr->region_index != memdev->region_index)
|
||||
continue;
|
||||
if (nfit_dcr->dcr->code == dcr->code)
|
||||
continue;
|
||||
rc = sprintf(buf, "%#x\n",
|
||||
be16_to_cpu(nfit_dcr->dcr->code));
|
||||
break;
|
||||
}
|
||||
if (rc != ENXIO)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
return rc;
|
||||
}
|
||||
static DEVICE_ATTR_RO(format1);
|
||||
|
||||
static ssize_t formats_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm));
|
||||
}
|
||||
static DEVICE_ATTR_RO(formats);
|
||||
|
||||
static ssize_t serial_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", dcr->serial_number);
|
||||
return sprintf(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number));
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -873,15 +1029,41 @@ static ssize_t flags_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(flags);
|
||||
|
||||
static ssize_t id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
|
||||
|
||||
if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID)
|
||||
return sprintf(buf, "%04x-%02x-%04x-%08x\n",
|
||||
be16_to_cpu(dcr->vendor_id),
|
||||
dcr->manufacturing_location,
|
||||
be16_to_cpu(dcr->manufacturing_date),
|
||||
be32_to_cpu(dcr->serial_number));
|
||||
else
|
||||
return sprintf(buf, "%04x-%08x\n",
|
||||
be16_to_cpu(dcr->vendor_id),
|
||||
be32_to_cpu(dcr->serial_number));
|
||||
}
|
||||
static DEVICE_ATTR_RO(id);
|
||||
|
||||
static struct attribute *acpi_nfit_dimm_attributes[] = {
|
||||
&dev_attr_handle.attr,
|
||||
&dev_attr_phys_id.attr,
|
||||
&dev_attr_vendor.attr,
|
||||
&dev_attr_device.attr,
|
||||
&dev_attr_format.attr,
|
||||
&dev_attr_serial.attr,
|
||||
&dev_attr_rev_id.attr,
|
||||
&dev_attr_subsystem_vendor.attr,
|
||||
&dev_attr_subsystem_device.attr,
|
||||
&dev_attr_subsystem_rev_id.attr,
|
||||
&dev_attr_format.attr,
|
||||
&dev_attr_formats.attr,
|
||||
&dev_attr_format1.attr,
|
||||
&dev_attr_serial.attr,
|
||||
&dev_attr_flags.attr,
|
||||
&dev_attr_id.attr,
|
||||
&dev_attr_family.attr,
|
||||
&dev_attr_dsm_mask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -889,11 +1071,13 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
|
||||
if (to_nfit_dcr(dev))
|
||||
return a->mode;
|
||||
else
|
||||
if (!to_nfit_dcr(dev))
|
||||
return 0;
|
||||
if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1)
|
||||
return 0;
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group acpi_nfit_dimm_attribute_group = {
|
||||
@@ -926,10 +1110,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;
|
||||
@@ -942,7 +1129,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);
|
||||
|
||||
@@ -955,8 +1170,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;
|
||||
@@ -979,9 +1194,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;
|
||||
|
||||
@@ -1010,14 +1234,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,
|
||||
@@ -2309,7 +2533,7 @@ static int acpi_nfit_add(struct acpi_device *adev)
|
||||
acpi_size sz;
|
||||
int rc;
|
||||
|
||||
status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz);
|
||||
status = acpi_get_table_with_size(ACPI_SIG_NFIT, 0, &tbl, &sz);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
/* This is ok, we could have an nvdimm hotplugged later */
|
||||
dev_dbg(dev, "failed to find NFIT at startup\n");
|
||||
@@ -2466,6 +2690,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)
|
||||
|
||||
+23
-8
@@ -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,15 +49,16 @@ enum nfit_uuids {
|
||||
NFIT_SPA_PDISK,
|
||||
NFIT_SPA_PCD,
|
||||
NFIT_DEV_BUS,
|
||||
NFIT_DEV_DIMM,
|
||||
NFIT_UUID_MAX,
|
||||
};
|
||||
|
||||
enum nfit_fic {
|
||||
NFIT_FIC_BYTE = 0x101, /* byte-addressable energy backed */
|
||||
NFIT_FIC_BLK = 0x201, /* block-addressable non-energy backed */
|
||||
NFIT_FIC_BYTEN = 0x301, /* byte-addressable non-energy backed */
|
||||
};
|
||||
/*
|
||||
* Region format interface codes are stored as an array of bytes in the
|
||||
* NFIT DIMM Control Region structure
|
||||
*/
|
||||
#define NFIT_FIC_BYTE cpu_to_be16(0x101) /* byte-addressable energy backed */
|
||||
#define NFIT_FIC_BLK cpu_to_be16(0x201) /* block-addressable non-energy backed */
|
||||
#define NFIT_FIC_BYTEN cpu_to_be16(0x301) /* byte-addressable non-energy backed */
|
||||
|
||||
enum {
|
||||
NFIT_BLK_READ_FLUSH = 1,
|
||||
@@ -109,7 +122,9 @@ struct nfit_mem {
|
||||
struct nfit_flush *nfit_flush;
|
||||
struct list_head list;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
unsigned long dsm_mask;
|
||||
int family;
|
||||
};
|
||||
|
||||
struct acpi_nfit_desc {
|
||||
@@ -132,8 +147,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;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
menuconfig DEV_DAX
|
||||
tristate "DAX: direct access to differentiated memory"
|
||||
default m if NVDIMM_DAX
|
||||
depends on TRANSPARENT_HUGEPAGE
|
||||
help
|
||||
Support raw access to differentiated (persistence, bandwidth,
|
||||
latency...) memory via an mmap(2) capable character
|
||||
device. Platform firmware or a device driver may identify a
|
||||
platform memory resource that is differentiated from the
|
||||
baseline memory pool. Mappings of a /dev/daxX.Y device impose
|
||||
restrictions that make the mapping behavior deterministic.
|
||||
|
||||
if DEV_DAX
|
||||
|
||||
config DEV_DAX_PMEM
|
||||
tristate "PMEM DAX: direct access to persistent memory"
|
||||
depends on NVDIMM_DAX
|
||||
default DEV_DAX
|
||||
help
|
||||
Support raw access to persistent memory. Note that this
|
||||
driver consumes memory ranges allocated and exported by the
|
||||
libnvdimm sub-system.
|
||||
|
||||
Say Y if unsure
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_DEV_DAX) += dax.o
|
||||
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
|
||||
|
||||
dax_pmem-y := pmem.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright(c) 2016 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.
|
||||
*/
|
||||
#ifndef __DAX_H__
|
||||
#define __DAX_H__
|
||||
struct device;
|
||||
struct resource;
|
||||
struct dax_region;
|
||||
void dax_region_put(struct dax_region *dax_region);
|
||||
struct dax_region *alloc_dax_region(struct device *parent,
|
||||
int region_id, struct resource *res, unsigned int align,
|
||||
void *addr, unsigned long flags);
|
||||
int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res,
|
||||
int count);
|
||||
#endif /* __DAX_H__ */
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright(c) 2016 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/percpu-refcount.h>
|
||||
#include <linux/memremap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pfn_t.h>
|
||||
#include "../nvdimm/pfn.h"
|
||||
#include "../nvdimm/nd.h"
|
||||
#include "dax.h"
|
||||
|
||||
struct dax_pmem {
|
||||
struct device *dev;
|
||||
struct percpu_ref ref;
|
||||
struct completion cmp;
|
||||
};
|
||||
|
||||
struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
|
||||
{
|
||||
return container_of(ref, struct dax_pmem, ref);
|
||||
}
|
||||
|
||||
static void dax_pmem_percpu_release(struct percpu_ref *ref)
|
||||
{
|
||||
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
|
||||
|
||||
dev_dbg(dax_pmem->dev, "%s\n", __func__);
|
||||
complete(&dax_pmem->cmp);
|
||||
}
|
||||
|
||||
static void dax_pmem_percpu_exit(void *data)
|
||||
{
|
||||
struct percpu_ref *ref = data;
|
||||
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
|
||||
|
||||
dev_dbg(dax_pmem->dev, "%s\n", __func__);
|
||||
percpu_ref_exit(ref);
|
||||
wait_for_completion(&dax_pmem->cmp);
|
||||
}
|
||||
|
||||
static void dax_pmem_percpu_kill(void *data)
|
||||
{
|
||||
struct percpu_ref *ref = data;
|
||||
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
|
||||
|
||||
dev_dbg(dax_pmem->dev, "%s\n", __func__);
|
||||
percpu_ref_kill(ref);
|
||||
}
|
||||
|
||||
static int dax_pmem_probe(struct device *dev)
|
||||
{
|
||||
int rc;
|
||||
void *addr;
|
||||
struct resource res;
|
||||
struct nd_pfn_sb *pfn_sb;
|
||||
struct dax_pmem *dax_pmem;
|
||||
struct nd_region *nd_region;
|
||||
struct nd_namespace_io *nsio;
|
||||
struct dax_region *dax_region;
|
||||
struct nd_namespace_common *ndns;
|
||||
struct nd_dax *nd_dax = to_nd_dax(dev);
|
||||
struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
|
||||
struct vmem_altmap __altmap, *altmap = NULL;
|
||||
|
||||
ndns = nvdimm_namespace_common_probe(dev);
|
||||
if (IS_ERR(ndns))
|
||||
return PTR_ERR(ndns);
|
||||
nsio = to_nd_namespace_io(&ndns->dev);
|
||||
|
||||
/* parse the 'pfn' info block via ->rw_bytes */
|
||||
devm_nsio_enable(dev, nsio);
|
||||
altmap = nvdimm_setup_pfn(nd_pfn, &res, &__altmap);
|
||||
if (IS_ERR(altmap))
|
||||
return PTR_ERR(altmap);
|
||||
devm_nsio_disable(dev, nsio);
|
||||
|
||||
pfn_sb = nd_pfn->pfn_sb;
|
||||
|
||||
if (!devm_request_mem_region(dev, nsio->res.start,
|
||||
resource_size(&nsio->res), dev_name(dev))) {
|
||||
dev_warn(dev, "could not reserve region %pR\n", &nsio->res);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL);
|
||||
if (!dax_pmem)
|
||||
return -ENOMEM;
|
||||
|
||||
dax_pmem->dev = dev;
|
||||
init_completion(&dax_pmem->cmp);
|
||||
rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0,
|
||||
GFP_KERNEL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = devm_add_action(dev, dax_pmem_percpu_exit, &dax_pmem->ref);
|
||||
if (rc) {
|
||||
dax_pmem_percpu_exit(&dax_pmem->ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
addr = devm_memremap_pages(dev, &res, &dax_pmem->ref, altmap);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
|
||||
rc = devm_add_action(dev, dax_pmem_percpu_kill, &dax_pmem->ref);
|
||||
if (rc) {
|
||||
dax_pmem_percpu_kill(&dax_pmem->ref);
|
||||
return rc;
|
||||
}
|
||||
|
||||
nd_region = to_nd_region(dev->parent);
|
||||
dax_region = alloc_dax_region(dev, nd_region->id, &res,
|
||||
le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
|
||||
if (!dax_region)
|
||||
return -ENOMEM;
|
||||
|
||||
/* TODO: support for subdividing a dax region... */
|
||||
rc = devm_create_dax_dev(dax_region, &res, 1);
|
||||
|
||||
/* child dax_dev instances now own the lifetime of the dax_region */
|
||||
dax_region_put(dax_region);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct nd_device_driver dax_pmem_driver = {
|
||||
.probe = dax_pmem_probe,
|
||||
.drv = {
|
||||
.name = "dax_pmem",
|
||||
},
|
||||
.type = ND_DRIVER_DAX_PMEM,
|
||||
};
|
||||
|
||||
static int __init dax_pmem_init(void)
|
||||
{
|
||||
return nd_driver_register(&dax_pmem_driver);
|
||||
}
|
||||
module_init(dax_pmem_init);
|
||||
|
||||
static void __exit dax_pmem_exit(void)
|
||||
{
|
||||
driver_unregister(&dax_pmem_driver.drv);
|
||||
}
|
||||
module_exit(dax_pmem_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
|
||||
@@ -88,4 +88,17 @@ config NVDIMM_PFN
|
||||
|
||||
Select Y if unsure
|
||||
|
||||
config NVDIMM_DAX
|
||||
bool "NVDIMM DAX: Raw access to persistent memory"
|
||||
default LIBNVDIMM
|
||||
depends on NVDIMM_PFN
|
||||
help
|
||||
Support raw device dax access to a persistent memory
|
||||
namespace. For environments that want to hard partition
|
||||
peristent memory, this capability provides a mechanism to
|
||||
sub-divide a namespace into character devices that can only be
|
||||
accessed via DAX (mmap(2)).
|
||||
|
||||
Select Y if unsure
|
||||
|
||||
endif
|
||||
|
||||
@@ -23,3 +23,4 @@ libnvdimm-y += label.o
|
||||
libnvdimm-$(CONFIG_ND_CLAIM) += claim.o
|
||||
libnvdimm-$(CONFIG_BTT) += btt_devs.o
|
||||
libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o
|
||||
libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o
|
||||
|
||||
+104
-110
@@ -21,19 +21,19 @@
|
||||
#include <linux/sizes.h>
|
||||
#include "nd.h"
|
||||
|
||||
struct nd_blk_device {
|
||||
struct request_queue *queue;
|
||||
struct gendisk *disk;
|
||||
struct nd_namespace_blk *nsblk;
|
||||
struct nd_blk_region *ndbr;
|
||||
size_t disk_size;
|
||||
u32 sector_size;
|
||||
u32 internal_lbasize;
|
||||
};
|
||||
|
||||
static u32 nd_blk_meta_size(struct nd_blk_device *blk_dev)
|
||||
static u32 nsblk_meta_size(struct nd_namespace_blk *nsblk)
|
||||
{
|
||||
return blk_dev->nsblk->lbasize - blk_dev->sector_size;
|
||||
return nsblk->lbasize - ((nsblk->lbasize >= 4096) ? 4096 : 512);
|
||||
}
|
||||
|
||||
static u32 nsblk_internal_lbasize(struct nd_namespace_blk *nsblk)
|
||||
{
|
||||
return roundup(nsblk->lbasize, INT_LBASIZE_ALIGNMENT);
|
||||
}
|
||||
|
||||
static u32 nsblk_sector_size(struct nd_namespace_blk *nsblk)
|
||||
{
|
||||
return nsblk->lbasize - nsblk_meta_size(nsblk);
|
||||
}
|
||||
|
||||
static resource_size_t to_dev_offset(struct nd_namespace_blk *nsblk,
|
||||
@@ -57,20 +57,29 @@ static resource_size_t to_dev_offset(struct nd_namespace_blk *nsblk,
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INTEGRITY
|
||||
static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev,
|
||||
struct bio_integrity_payload *bip, u64 lba,
|
||||
int rw)
|
||||
static struct nd_blk_region *to_ndbr(struct nd_namespace_blk *nsblk)
|
||||
{
|
||||
unsigned int len = nd_blk_meta_size(blk_dev);
|
||||
struct nd_region *nd_region;
|
||||
struct device *parent;
|
||||
|
||||
parent = nsblk->common.dev.parent;
|
||||
nd_region = container_of(parent, struct nd_region, dev);
|
||||
return container_of(nd_region, struct nd_blk_region, nd_region);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INTEGRITY
|
||||
static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
|
||||
struct bio_integrity_payload *bip, u64 lba, int rw)
|
||||
{
|
||||
struct nd_blk_region *ndbr = to_ndbr(nsblk);
|
||||
unsigned int len = nsblk_meta_size(nsblk);
|
||||
resource_size_t dev_offset, ns_offset;
|
||||
struct nd_namespace_blk *nsblk;
|
||||
struct nd_blk_region *ndbr;
|
||||
u32 internal_lbasize, sector_size;
|
||||
int err = 0;
|
||||
|
||||
nsblk = blk_dev->nsblk;
|
||||
ndbr = blk_dev->ndbr;
|
||||
ns_offset = lba * blk_dev->internal_lbasize + blk_dev->sector_size;
|
||||
internal_lbasize = nsblk_internal_lbasize(nsblk);
|
||||
sector_size = nsblk_sector_size(nsblk);
|
||||
ns_offset = lba * internal_lbasize + sector_size;
|
||||
dev_offset = to_dev_offset(nsblk, ns_offset, len);
|
||||
if (dev_offset == SIZE_MAX)
|
||||
return -EIO;
|
||||
@@ -104,25 +113,26 @@ static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev,
|
||||
}
|
||||
|
||||
#else /* CONFIG_BLK_DEV_INTEGRITY */
|
||||
static int nd_blk_rw_integrity(struct nd_blk_device *blk_dev,
|
||||
struct bio_integrity_payload *bip, u64 lba,
|
||||
int rw)
|
||||
static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
|
||||
struct bio_integrity_payload *bip, u64 lba, int rw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nd_blk_do_bvec(struct nd_blk_device *blk_dev,
|
||||
struct bio_integrity_payload *bip, struct page *page,
|
||||
unsigned int len, unsigned int off, int rw,
|
||||
sector_t sector)
|
||||
static int nsblk_do_bvec(struct nd_namespace_blk *nsblk,
|
||||
struct bio_integrity_payload *bip, struct page *page,
|
||||
unsigned int len, unsigned int off, int rw, sector_t sector)
|
||||
{
|
||||
struct nd_blk_region *ndbr = blk_dev->ndbr;
|
||||
struct nd_blk_region *ndbr = to_ndbr(nsblk);
|
||||
resource_size_t dev_offset, ns_offset;
|
||||
u32 internal_lbasize, sector_size;
|
||||
int err = 0;
|
||||
void *iobuf;
|
||||
u64 lba;
|
||||
|
||||
internal_lbasize = nsblk_internal_lbasize(nsblk);
|
||||
sector_size = nsblk_sector_size(nsblk);
|
||||
while (len) {
|
||||
unsigned int cur_len;
|
||||
|
||||
@@ -132,11 +142,11 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev,
|
||||
* Block Window setup/move steps. the do_io routine is capable
|
||||
* of handling len <= PAGE_SIZE.
|
||||
*/
|
||||
cur_len = bip ? min(len, blk_dev->sector_size) : len;
|
||||
cur_len = bip ? min(len, sector_size) : len;
|
||||
|
||||
lba = div_u64(sector << SECTOR_SHIFT, blk_dev->sector_size);
|
||||
ns_offset = lba * blk_dev->internal_lbasize;
|
||||
dev_offset = to_dev_offset(blk_dev->nsblk, ns_offset, cur_len);
|
||||
lba = div_u64(sector << SECTOR_SHIFT, sector_size);
|
||||
ns_offset = lba * internal_lbasize;
|
||||
dev_offset = to_dev_offset(nsblk, ns_offset, cur_len);
|
||||
if (dev_offset == SIZE_MAX)
|
||||
return -EIO;
|
||||
|
||||
@@ -147,13 +157,13 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev,
|
||||
return err;
|
||||
|
||||
if (bip) {
|
||||
err = nd_blk_rw_integrity(blk_dev, bip, lba, rw);
|
||||
err = nd_blk_rw_integrity(nsblk, bip, lba, rw);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
len -= cur_len;
|
||||
off += cur_len;
|
||||
sector += blk_dev->sector_size >> SECTOR_SHIFT;
|
||||
sector += sector_size >> SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -161,10 +171,8 @@ static int nd_blk_do_bvec(struct nd_blk_device *blk_dev,
|
||||
|
||||
static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
struct block_device *bdev = bio->bi_bdev;
|
||||
struct gendisk *disk = bdev->bd_disk;
|
||||
struct bio_integrity_payload *bip;
|
||||
struct nd_blk_device *blk_dev;
|
||||
struct nd_namespace_blk *nsblk;
|
||||
struct bvec_iter iter;
|
||||
unsigned long start;
|
||||
struct bio_vec bvec;
|
||||
@@ -183,17 +191,17 @@ static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio)
|
||||
}
|
||||
|
||||
bip = bio_integrity(bio);
|
||||
blk_dev = disk->private_data;
|
||||
nsblk = q->queuedata;
|
||||
rw = bio_data_dir(bio);
|
||||
do_acct = nd_iostat_start(bio, &start);
|
||||
bio_for_each_segment(bvec, bio, iter) {
|
||||
unsigned int len = bvec.bv_len;
|
||||
|
||||
BUG_ON(len > PAGE_SIZE);
|
||||
err = nd_blk_do_bvec(blk_dev, bip, bvec.bv_page, len,
|
||||
bvec.bv_offset, rw, iter.bi_sector);
|
||||
err = nsblk_do_bvec(nsblk, bip, bvec.bv_page, len,
|
||||
bvec.bv_offset, rw, iter.bi_sector);
|
||||
if (err) {
|
||||
dev_info(&blk_dev->nsblk->common.dev,
|
||||
dev_dbg(&nsblk->common.dev,
|
||||
"io error in %s sector %lld, len %d,\n",
|
||||
(rw == READ) ? "READ" : "WRITE",
|
||||
(unsigned long long) iter.bi_sector, len);
|
||||
@@ -209,17 +217,16 @@ static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio)
|
||||
return BLK_QC_T_NONE;
|
||||
}
|
||||
|
||||
static int nd_blk_rw_bytes(struct nd_namespace_common *ndns,
|
||||
static int nsblk_rw_bytes(struct nd_namespace_common *ndns,
|
||||
resource_size_t offset, void *iobuf, size_t n, int rw)
|
||||
{
|
||||
struct nd_blk_device *blk_dev = dev_get_drvdata(ndns->claim);
|
||||
struct nd_namespace_blk *nsblk = blk_dev->nsblk;
|
||||
struct nd_blk_region *ndbr = blk_dev->ndbr;
|
||||
struct nd_namespace_blk *nsblk = to_nd_namespace_blk(&ndns->dev);
|
||||
struct nd_blk_region *ndbr = to_ndbr(nsblk);
|
||||
resource_size_t dev_offset;
|
||||
|
||||
dev_offset = to_dev_offset(nsblk, offset, n);
|
||||
|
||||
if (unlikely(offset + n > blk_dev->disk_size)) {
|
||||
if (unlikely(offset + n > nsblk->size)) {
|
||||
dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
@@ -235,51 +242,65 @@ static const struct block_device_operations nd_blk_fops = {
|
||||
.revalidate_disk = nvdimm_revalidate_disk,
|
||||
};
|
||||
|
||||
static int nd_blk_attach_disk(struct nd_namespace_common *ndns,
|
||||
struct nd_blk_device *blk_dev)
|
||||
static void nd_blk_release_queue(void *q)
|
||||
{
|
||||
blk_cleanup_queue(q);
|
||||
}
|
||||
|
||||
static void nd_blk_release_disk(void *disk)
|
||||
{
|
||||
del_gendisk(disk);
|
||||
put_disk(disk);
|
||||
}
|
||||
|
||||
static int nsblk_attach_disk(struct nd_namespace_blk *nsblk)
|
||||
{
|
||||
struct device *dev = &nsblk->common.dev;
|
||||
resource_size_t available_disk_size;
|
||||
struct request_queue *q;
|
||||
struct gendisk *disk;
|
||||
u64 internal_nlba;
|
||||
|
||||
internal_nlba = div_u64(blk_dev->disk_size, blk_dev->internal_lbasize);
|
||||
available_disk_size = internal_nlba * blk_dev->sector_size;
|
||||
internal_nlba = div_u64(nsblk->size, nsblk_internal_lbasize(nsblk));
|
||||
available_disk_size = internal_nlba * nsblk_sector_size(nsblk);
|
||||
|
||||
blk_dev->queue = blk_alloc_queue(GFP_KERNEL);
|
||||
if (!blk_dev->queue)
|
||||
q = blk_alloc_queue(GFP_KERNEL);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
blk_queue_make_request(blk_dev->queue, nd_blk_make_request);
|
||||
blk_queue_max_hw_sectors(blk_dev->queue, UINT_MAX);
|
||||
blk_queue_bounce_limit(blk_dev->queue, BLK_BOUNCE_ANY);
|
||||
blk_queue_logical_block_size(blk_dev->queue, blk_dev->sector_size);
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, blk_dev->queue);
|
||||
|
||||
disk = blk_dev->disk = alloc_disk(0);
|
||||
if (!disk) {
|
||||
blk_cleanup_queue(blk_dev->queue);
|
||||
if (devm_add_action(dev, nd_blk_release_queue, q)) {
|
||||
blk_cleanup_queue(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
disk->driverfs_dev = &ndns->dev;
|
||||
blk_queue_make_request(q, nd_blk_make_request);
|
||||
blk_queue_max_hw_sectors(q, UINT_MAX);
|
||||
blk_queue_bounce_limit(q, BLK_BOUNCE_ANY);
|
||||
blk_queue_logical_block_size(q, nsblk_sector_size(nsblk));
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
|
||||
q->queuedata = nsblk;
|
||||
|
||||
disk = alloc_disk(0);
|
||||
if (!disk)
|
||||
return -ENOMEM;
|
||||
if (devm_add_action(dev, nd_blk_release_disk, disk)) {
|
||||
put_disk(disk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
disk->driverfs_dev = dev;
|
||||
disk->first_minor = 0;
|
||||
disk->fops = &nd_blk_fops;
|
||||
disk->private_data = blk_dev;
|
||||
disk->queue = blk_dev->queue;
|
||||
disk->queue = q;
|
||||
disk->flags = GENHD_FL_EXT_DEVT;
|
||||
nvdimm_namespace_disk_name(ndns, disk->disk_name);
|
||||
nvdimm_namespace_disk_name(&nsblk->common, disk->disk_name);
|
||||
set_capacity(disk, 0);
|
||||
add_disk(disk);
|
||||
|
||||
if (nd_blk_meta_size(blk_dev)) {
|
||||
int rc = nd_integrity_init(disk, nd_blk_meta_size(blk_dev));
|
||||
if (nsblk_meta_size(nsblk)) {
|
||||
int rc = nd_integrity_init(disk, nsblk_meta_size(nsblk));
|
||||
|
||||
if (rc) {
|
||||
del_gendisk(disk);
|
||||
put_disk(disk);
|
||||
blk_cleanup_queue(blk_dev->queue);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
|
||||
@@ -291,56 +312,29 @@ static int nd_blk_probe(struct device *dev)
|
||||
{
|
||||
struct nd_namespace_common *ndns;
|
||||
struct nd_namespace_blk *nsblk;
|
||||
struct nd_blk_device *blk_dev;
|
||||
int rc;
|
||||
|
||||
ndns = nvdimm_namespace_common_probe(dev);
|
||||
if (IS_ERR(ndns))
|
||||
return PTR_ERR(ndns);
|
||||
|
||||
blk_dev = kzalloc(sizeof(*blk_dev), GFP_KERNEL);
|
||||
if (!blk_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
nsblk = to_nd_namespace_blk(&ndns->dev);
|
||||
blk_dev->disk_size = nvdimm_namespace_capacity(ndns);
|
||||
blk_dev->ndbr = to_nd_blk_region(dev->parent);
|
||||
blk_dev->nsblk = to_nd_namespace_blk(&ndns->dev);
|
||||
blk_dev->internal_lbasize = roundup(nsblk->lbasize,
|
||||
INT_LBASIZE_ALIGNMENT);
|
||||
blk_dev->sector_size = ((nsblk->lbasize >= 4096) ? 4096 : 512);
|
||||
dev_set_drvdata(dev, blk_dev);
|
||||
nsblk->size = nvdimm_namespace_capacity(ndns);
|
||||
dev_set_drvdata(dev, nsblk);
|
||||
|
||||
ndns->rw_bytes = nd_blk_rw_bytes;
|
||||
ndns->rw_bytes = nsblk_rw_bytes;
|
||||
if (is_nd_btt(dev))
|
||||
rc = nvdimm_namespace_attach_btt(ndns);
|
||||
else if (nd_btt_probe(ndns, blk_dev) == 0) {
|
||||
return nvdimm_namespace_attach_btt(ndns);
|
||||
else if (nd_btt_probe(dev, ndns) == 0) {
|
||||
/* we'll come back as btt-blk */
|
||||
rc = -ENXIO;
|
||||
return -ENXIO;
|
||||
} else
|
||||
rc = nd_blk_attach_disk(ndns, blk_dev);
|
||||
if (rc)
|
||||
kfree(blk_dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void nd_blk_detach_disk(struct nd_blk_device *blk_dev)
|
||||
{
|
||||
del_gendisk(blk_dev->disk);
|
||||
put_disk(blk_dev->disk);
|
||||
blk_cleanup_queue(blk_dev->queue);
|
||||
return nsblk_attach_disk(nsblk);
|
||||
}
|
||||
|
||||
static int nd_blk_remove(struct device *dev)
|
||||
{
|
||||
struct nd_blk_device *blk_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (is_nd_btt(dev))
|
||||
nvdimm_namespace_detach_btt(to_nd_btt(dev)->ndns);
|
||||
else
|
||||
nd_blk_detach_disk(blk_dev);
|
||||
kfree(blk_dev);
|
||||
|
||||
nvdimm_namespace_detach_btt(to_nd_btt(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+12
-14
@@ -1306,7 +1306,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
|
||||
struct btt *btt;
|
||||
struct device *dev = &nd_btt->dev;
|
||||
|
||||
btt = kzalloc(sizeof(struct btt), GFP_KERNEL);
|
||||
btt = devm_kzalloc(dev, sizeof(struct btt), GFP_KERNEL);
|
||||
if (!btt)
|
||||
return NULL;
|
||||
|
||||
@@ -1321,13 +1321,13 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
|
||||
ret = discover_arenas(btt);
|
||||
if (ret) {
|
||||
dev_err(dev, "init: error in arena_discover: %d\n", ret);
|
||||
goto out_free;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (btt->init_state != INIT_READY && nd_region->ro) {
|
||||
dev_info(dev, "%s is read-only, unable to init btt metadata\n",
|
||||
dev_name(&nd_region->dev));
|
||||
goto out_free;
|
||||
return NULL;
|
||||
} else if (btt->init_state != INIT_READY) {
|
||||
btt->num_arenas = (rawsize / ARENA_MAX_SIZE) +
|
||||
((rawsize % ARENA_MAX_SIZE) ? 1 : 0);
|
||||
@@ -1337,29 +1337,25 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
|
||||
ret = create_arenas(btt);
|
||||
if (ret) {
|
||||
dev_info(dev, "init: create_arenas: %d\n", ret);
|
||||
goto out_free;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = btt_meta_init(btt);
|
||||
if (ret) {
|
||||
dev_err(dev, "init: error in meta_init: %d\n", ret);
|
||||
goto out_free;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btt_blk_init(btt);
|
||||
if (ret) {
|
||||
dev_err(dev, "init: error in blk_init: %d\n", ret);
|
||||
goto out_free;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
btt_debugfs_init(btt);
|
||||
|
||||
return btt;
|
||||
|
||||
out_free:
|
||||
kfree(btt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1377,7 +1373,6 @@ static void btt_fini(struct btt *btt)
|
||||
btt_blk_cleanup(btt);
|
||||
free_arenas(btt);
|
||||
debugfs_remove_recursive(btt->debugfs_dir);
|
||||
kfree(btt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1388,11 +1383,15 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns)
|
||||
struct btt *btt;
|
||||
size_t rawsize;
|
||||
|
||||
if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize)
|
||||
if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) {
|
||||
dev_dbg(&nd_btt->dev, "incomplete btt configuration\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rawsize = nvdimm_namespace_capacity(ndns) - SZ_4K;
|
||||
if (rawsize < ARENA_MIN_SIZE) {
|
||||
dev_dbg(&nd_btt->dev, "%s must be at least %ld bytes\n",
|
||||
dev_name(&ndns->dev), ARENA_MIN_SIZE + SZ_4K);
|
||||
return -ENXIO;
|
||||
}
|
||||
nd_region = to_nd_region(nd_btt->dev.parent);
|
||||
@@ -1406,9 +1405,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns)
|
||||
}
|
||||
EXPORT_SYMBOL(nvdimm_namespace_attach_btt);
|
||||
|
||||
int nvdimm_namespace_detach_btt(struct nd_namespace_common *ndns)
|
||||
int nvdimm_namespace_detach_btt(struct nd_btt *nd_btt)
|
||||
{
|
||||
struct nd_btt *nd_btt = to_nd_btt(ndns->claim);
|
||||
struct btt *btt = nd_btt->btt;
|
||||
|
||||
btt_fini(btt);
|
||||
|
||||
+11
-13
@@ -273,10 +273,10 @@ static int __nd_btt_probe(struct nd_btt *nd_btt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata)
|
||||
int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns)
|
||||
{
|
||||
int rc;
|
||||
struct device *dev;
|
||||
struct device *btt_dev;
|
||||
struct btt_sb *btt_sb;
|
||||
struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
|
||||
|
||||
@@ -284,21 +284,19 @@ int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata)
|
||||
return -ENODEV;
|
||||
|
||||
nvdimm_bus_lock(&ndns->dev);
|
||||
dev = __nd_btt_create(nd_region, 0, NULL, ndns);
|
||||
btt_dev = __nd_btt_create(nd_region, 0, NULL, ndns);
|
||||
nvdimm_bus_unlock(&ndns->dev);
|
||||
if (!dev)
|
||||
if (!btt_dev)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
btt_sb = kzalloc(sizeof(*btt_sb), GFP_KERNEL);
|
||||
rc = __nd_btt_probe(to_nd_btt(dev), ndns, btt_sb);
|
||||
kfree(btt_sb);
|
||||
dev_dbg(&ndns->dev, "%s: btt: %s\n", __func__,
|
||||
rc == 0 ? dev_name(dev) : "<none>");
|
||||
btt_sb = devm_kzalloc(dev, sizeof(*btt_sb), GFP_KERNEL);
|
||||
rc = __nd_btt_probe(to_nd_btt(btt_dev), ndns, btt_sb);
|
||||
dev_dbg(dev, "%s: btt: %s\n", __func__,
|
||||
rc == 0 ? dev_name(btt_dev) : "<none>");
|
||||
if (rc < 0) {
|
||||
struct nd_btt *nd_btt = to_nd_btt(dev);
|
||||
struct nd_btt *nd_btt = to_nd_btt(btt_dev);
|
||||
|
||||
__nd_detach_ndns(dev, &nd_btt->ndns);
|
||||
put_device(dev);
|
||||
__nd_detach_ndns(btt_dev, &nd_btt->ndns);
|
||||
put_device(btt_dev);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
+55
-8
@@ -40,6 +40,8 @@ static int to_nd_device_type(struct device *dev)
|
||||
return ND_DEVICE_REGION_PMEM;
|
||||
else if (is_nd_blk(dev))
|
||||
return ND_DEVICE_REGION_BLK;
|
||||
else if (is_nd_dax(dev))
|
||||
return ND_DEVICE_DAX_PMEM;
|
||||
else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent))
|
||||
return nd_region_to_nstype(to_nd_region(dev->parent));
|
||||
|
||||
@@ -122,9 +124,10 @@ static int nvdimm_bus_remove(struct device *dev)
|
||||
struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
|
||||
struct module *provider = to_bus_provider(dev);
|
||||
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
rc = nd_drv->remove(dev);
|
||||
if (nd_drv->remove)
|
||||
rc = nd_drv->remove(dev);
|
||||
nd_region_disable(nvdimm_bus, dev);
|
||||
|
||||
dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
|
||||
@@ -246,6 +249,8 @@ static void nd_async_device_unregister(void *d, async_cookie_t cookie)
|
||||
|
||||
void __nd_device_register(struct device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
dev->bus = &nvdimm_bus_type;
|
||||
get_device(dev);
|
||||
async_schedule_domain(nd_async_device_register, dev,
|
||||
@@ -292,8 +297,8 @@ int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!nd_drv->probe || !nd_drv->remove) {
|
||||
pr_debug("->probe() and ->remove() must be specified\n");
|
||||
if (!nd_drv->probe) {
|
||||
pr_debug("%s ->probe() must be specified\n", mod_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -439,6 +444,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)
|
||||
@@ -473,6 +484,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)
|
||||
@@ -500,6 +517,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;
|
||||
@@ -522,6 +543,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;
|
||||
}
|
||||
@@ -588,25 +615,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) */
|
||||
@@ -616,6 +649,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));
|
||||
@@ -643,6 +677,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,
|
||||
@@ -783,6 +827,9 @@ int __init nvdimm_bus_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct nd_smart_payload) != 128);
|
||||
BUILD_BUG_ON(sizeof(struct nd_smart_threshold_payload) != 8);
|
||||
|
||||
rc = bus_register(&nvdimm_bus_type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
+84
-2
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/pmem.h>
|
||||
#include "nd-core.h"
|
||||
#include "pfn.h"
|
||||
#include "btt.h"
|
||||
@@ -84,12 +85,33 @@ static bool is_idle(struct device *dev, struct nd_namespace_common *ndns)
|
||||
seed = nd_region->btt_seed;
|
||||
else if (is_nd_pfn(dev))
|
||||
seed = nd_region->pfn_seed;
|
||||
else if (is_nd_dax(dev))
|
||||
seed = nd_region->dax_seed;
|
||||
|
||||
if (seed == dev || ndns || dev->driver)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct nd_pfn *to_nd_pfn_safe(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* pfn device attributes are re-used by dax device instances, so we
|
||||
* need to be careful to correct device-to-nd_pfn conversion.
|
||||
*/
|
||||
if (is_nd_pfn(dev))
|
||||
return to_nd_pfn(dev);
|
||||
|
||||
if (is_nd_dax(dev)) {
|
||||
struct nd_dax *nd_dax = to_nd_dax(dev);
|
||||
|
||||
return &nd_dax->nd_pfn;
|
||||
}
|
||||
|
||||
WARN_ON(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nd_detach_and_reset(struct device *dev,
|
||||
struct nd_namespace_common **_ndns)
|
||||
{
|
||||
@@ -103,8 +125,8 @@ static void nd_detach_and_reset(struct device *dev,
|
||||
nd_btt->lbasize = 0;
|
||||
kfree(nd_btt->uuid);
|
||||
nd_btt->uuid = NULL;
|
||||
} else if (is_nd_pfn(dev)) {
|
||||
struct nd_pfn *nd_pfn = to_nd_pfn(dev);
|
||||
} else if (is_nd_pfn(dev) || is_nd_dax(dev)) {
|
||||
struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);
|
||||
|
||||
kfree(nd_pfn->uuid);
|
||||
nd_pfn->uuid = NULL;
|
||||
@@ -199,3 +221,63 @@ u64 nd_sb_checksum(struct nd_gen_sb *nd_gen_sb)
|
||||
return sum;
|
||||
}
|
||||
EXPORT_SYMBOL(nd_sb_checksum);
|
||||
|
||||
static int nsio_rw_bytes(struct nd_namespace_common *ndns,
|
||||
resource_size_t offset, void *buf, size_t size, int rw)
|
||||
{
|
||||
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
|
||||
|
||||
if (unlikely(offset + size > nsio->size)) {
|
||||
dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (rw == READ) {
|
||||
unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512);
|
||||
|
||||
if (unlikely(is_bad_pmem(&nsio->bb, offset / 512, sz_align)))
|
||||
return -EIO;
|
||||
return memcpy_from_pmem(buf, nsio->addr + offset, size);
|
||||
} else {
|
||||
memcpy_to_pmem(nsio->addr + offset, buf, size);
|
||||
wmb_pmem();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio)
|
||||
{
|
||||
struct resource *res = &nsio->res;
|
||||
struct nd_namespace_common *ndns = &nsio->common;
|
||||
|
||||
nsio->size = resource_size(res);
|
||||
if (!devm_request_mem_region(dev, res->start, resource_size(res),
|
||||
dev_name(dev))) {
|
||||
dev_warn(dev, "could not reserve region %pR\n", res);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ndns->rw_bytes = nsio_rw_bytes;
|
||||
if (devm_init_badblocks(dev, &nsio->bb))
|
||||
return -ENOMEM;
|
||||
nvdimm_badblocks_populate(to_nd_region(ndns->dev.parent), &nsio->bb,
|
||||
&nsio->res);
|
||||
|
||||
nsio->addr = devm_memremap(dev, res->start, resource_size(res),
|
||||
ARCH_MEMREMAP_PMEM);
|
||||
if (IS_ERR(nsio->addr))
|
||||
return PTR_ERR(nsio->addr);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_nsio_enable);
|
||||
|
||||
void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio)
|
||||
{
|
||||
struct resource *res = &nsio->res;
|
||||
|
||||
devm_memunmap(dev, nsio->addr);
|
||||
devm_exit_badblocks(dev, &nsio->bb);
|
||||
devm_release_mem_region(dev, res->start, resource_size(res));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_nsio_disable);
|
||||
|
||||
@@ -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;
|
||||
@@ -648,6 +648,9 @@ static __exit void libnvdimm_exit(void)
|
||||
nd_region_exit();
|
||||
nvdimm_exit();
|
||||
nvdimm_bus_exit();
|
||||
nd_region_devs_exit();
|
||||
nvdimm_devs_exit();
|
||||
ida_destroy(&nd_ida);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright(c) 2013-2016 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/device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include "nd-core.h"
|
||||
#include "pfn.h"
|
||||
#include "nd.h"
|
||||
|
||||
static void nd_dax_release(struct device *dev)
|
||||
{
|
||||
struct nd_region *nd_region = to_nd_region(dev->parent);
|
||||
struct nd_dax *nd_dax = to_nd_dax(dev);
|
||||
struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
nd_detach_ndns(dev, &nd_pfn->ndns);
|
||||
ida_simple_remove(&nd_region->dax_ida, nd_pfn->id);
|
||||
kfree(nd_pfn->uuid);
|
||||
kfree(nd_dax);
|
||||
}
|
||||
|
||||
static struct device_type nd_dax_device_type = {
|
||||
.name = "nd_dax",
|
||||
.release = nd_dax_release,
|
||||
};
|
||||
|
||||
bool is_nd_dax(struct device *dev)
|
||||
{
|
||||
return dev ? dev->type == &nd_dax_device_type : false;
|
||||
}
|
||||
EXPORT_SYMBOL(is_nd_dax);
|
||||
|
||||
struct nd_dax *to_nd_dax(struct device *dev)
|
||||
{
|
||||
struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev);
|
||||
|
||||
WARN_ON(!is_nd_dax(dev));
|
||||
return nd_dax;
|
||||
}
|
||||
EXPORT_SYMBOL(to_nd_dax);
|
||||
|
||||
static const struct attribute_group *nd_dax_attribute_groups[] = {
|
||||
&nd_pfn_attribute_group,
|
||||
&nd_device_attribute_group,
|
||||
&nd_numa_attribute_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region)
|
||||
{
|
||||
struct nd_pfn *nd_pfn;
|
||||
struct nd_dax *nd_dax;
|
||||
struct device *dev;
|
||||
|
||||
nd_dax = kzalloc(sizeof(*nd_dax), GFP_KERNEL);
|
||||
if (!nd_dax)
|
||||
return NULL;
|
||||
|
||||
nd_pfn = &nd_dax->nd_pfn;
|
||||
nd_pfn->id = ida_simple_get(&nd_region->dax_ida, 0, 0, GFP_KERNEL);
|
||||
if (nd_pfn->id < 0) {
|
||||
kfree(nd_dax);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = &nd_pfn->dev;
|
||||
dev_set_name(dev, "dax%d.%d", nd_region->id, nd_pfn->id);
|
||||
dev->groups = nd_dax_attribute_groups;
|
||||
dev->type = &nd_dax_device_type;
|
||||
dev->parent = &nd_region->dev;
|
||||
|
||||
return nd_dax;
|
||||
}
|
||||
|
||||
struct device *nd_dax_create(struct nd_region *nd_region)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
struct nd_dax *nd_dax;
|
||||
|
||||
if (!is_nd_pmem(&nd_region->dev))
|
||||
return NULL;
|
||||
|
||||
nd_dax = nd_dax_alloc(nd_region);
|
||||
if (nd_dax)
|
||||
dev = nd_pfn_devinit(&nd_dax->nd_pfn, NULL);
|
||||
__nd_device_register(dev);
|
||||
return dev;
|
||||
}
|
||||
|
||||
int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns)
|
||||
{
|
||||
int rc;
|
||||
struct nd_dax *nd_dax;
|
||||
struct device *dax_dev;
|
||||
struct nd_pfn *nd_pfn;
|
||||
struct nd_pfn_sb *pfn_sb;
|
||||
struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
|
||||
|
||||
if (ndns->force_raw)
|
||||
return -ENODEV;
|
||||
|
||||
nvdimm_bus_lock(&ndns->dev);
|
||||
nd_dax = nd_dax_alloc(nd_region);
|
||||
nd_pfn = &nd_dax->nd_pfn;
|
||||
dax_dev = nd_pfn_devinit(nd_pfn, ndns);
|
||||
nvdimm_bus_unlock(&ndns->dev);
|
||||
if (!dax_dev)
|
||||
return -ENOMEM;
|
||||
pfn_sb = devm_kzalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
|
||||
nd_pfn->pfn_sb = pfn_sb;
|
||||
rc = nd_pfn_validate(nd_pfn, DAX_SIG);
|
||||
dev_dbg(dev, "%s: dax: %s\n", __func__,
|
||||
rc == 0 ? dev_name(dax_dev) : "<none>");
|
||||
if (rc < 0) {
|
||||
__nd_detach_ndns(dax_dev, &nd_pfn->ndns);
|
||||
put_device(dax_dev);
|
||||
} else
|
||||
__nd_device_register(dax_dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(nd_dax_probe);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user