![Linus Torvalds](/assets/img/avatar_default.png)
* Replace the /sys/class/dax device model with /sys/bus/dax, and include a compat driver so distributions can opt-in to the new ABI. * Allow for an alternative driver for the device-dax address-range * Introduce the 'kmem' driver to hotplug / assign a device-dax address-range to the core-mm. * Arrange for the device-dax target-node to be onlined so that the newly added memory range can be uniquely referenced by numa apis. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJchWpGAAoJEB7SkWpmfYgCJk8P/0Q1DINszUDO/vKjJ09cDs9P Jw3it6GBIL50rDOu9QdcprSpwYDD0h1mLAV/m6oa3bVO+p4uWGvnxaxRx2HN2c/v vhZFtUDpHlqR63vzWMNVKRprYixCRJDUr6xQhhCcE3ak/ELN6w7LWfikKVWv15UL MfR96IQU38f+xRda/zSXnL9606Dvkvu/inEHj84lRcHIwj3sQAUalrE8bR3O32gZ bDg/l5kzT49o8ZXUo/TegvRSSSZpJmOl2DD0RW+ax5q3NI2bOXFrVDUKBKxf/hcQ E/V9i57TrqQx0GqRhnU7rN/v53cFZGGs31TEEIB/xs3bzCnADxwXcjL5b5K005J6 vJjBA2ODBewHFK3uVx46Hy1iV4eCtZWj4QrMnrjdSrjXOfbF5GTbWOhPFgoq7TWf S7VqFEf3I2gDPaMq4o8Ej1kLH4HMYeor2NSOZjyvGn87rSZ3ZIQguwbaNIVl+itz gdDt0ZOU0BgOBkV+rZIeZDaGdloWCHcDPL15CkZaOZyzdWhfEZ7dod6ad+9udilU EUPH62RgzXZtfm5zpebYyjNVLbb9pLZ0nT+UypyGR6zqWx1SqU3mXi63NFXPco+x XA9j//edPeI6NHg2CXLEh8DLuCg3dG1zWRJANkiF+niBwyCR8CHtGWAoY6soXbKe 2UrXGcIfXxyJ8V9v8v4q =hfa3 -----END PGP SIGNATURE----- Merge tag 'devdax-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm Pull device-dax updates from Dan Williams: "New device-dax infrastructure to allow persistent memory and other "reserved" / performance differentiated memories, to be assigned to the core-mm as "System RAM". Some users want to use persistent memory as additional volatile memory. They are willing to cope with potential performance differences, for example between DRAM and 3D Xpoint, and want to use typical Linux memory management apis rather than a userspace memory allocator layered over an mmap() of a dax file. The administration model is to decide how much Persistent Memory (pmem) to use as System RAM, create a device-dax-mode namespace of that size, and then assign it to the core-mm. The rationale for device-dax is that it is a generic memory-mapping driver that can be layered over any "special purpose" memory, not just pmem. On subsequent boots udev rules can be used to restore the memory assignment. One implication of using pmem as RAM is that mlock() no longer keeps data off persistent media. For this reason it is recommended to enable NVDIMM Security (previously merged for 5.0) to encrypt pmem contents at rest. We considered making this recommendation an actively enforced requirement, but in the end decided to leave it as a distribution / administrator policy to allow for emulation and test environments that lack security capable NVDIMMs. Summary: - Replace the /sys/class/dax device model with /sys/bus/dax, and include a compat driver so distributions can opt-in to the new ABI. - Allow for an alternative driver for the device-dax address-range - Introduce the 'kmem' driver to hotplug / assign a device-dax address-range to the core-mm. - Arrange for the device-dax target-node to be onlined so that the newly added memory range can be uniquely referenced by numa apis" NOTE! I'm not entirely happy with the whole "PMEM as RAM" model because we currently have special - and very annoying rules in the kernel about accessing PMEM only with the "MC safe" accessors, because machine checks inside the regular repeat string copy functions can be fatal in some (not described) circumstances. And apparently the PMEM modules can cause that a lot more than regular RAM. The argument is that this happens because PMEM doesn't necessarily get scrubbed at boot like RAM does, but that is planned to be added for the user space tooling. Quoting Dan from another email: "The exposure can be reduced in the volatile-RAM case by scanning for and clearing errors before it is onlined as RAM. The userspace tooling for that can be in place before v5.1-final. There's also runtime notifications of errors via acpi_nfit_uc_error_notify() from background scrubbers on the DIMM devices. With that mechanism the kernel could proactively clear newly discovered poison in the volatile case, but that would be additional development more suitable for v5.2. I understand the concern, and the need to highlight this issue by tapping the brakes on feature development, but I don't see PMEM as RAM making the situation worse when the exposure is also there via DAX in the PMEM case. Volatile-RAM is arguably a safer use case since it's possible to repair pages where the persistent case needs active application coordination" * tag 'devdax-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: device-dax: "Hotplug" persistent memory for use like normal RAM mm/resource: Let walk_system_ram_range() search child resources mm/memory-hotplug: Allow memory resources to be children mm/resource: Move HMM pr_debug() deeper into resource code mm/resource: Return real error codes from walk failures device-dax: Add a 'modalias' attribute to DAX 'bus' devices device-dax: Add a 'target_node' attribute device-dax: Auto-bind device after successful new_id acpi/nfit, device-dax: Identify differentiated memory with a unique numa-node device-dax: Add /sys/class/dax backwards compatibility device-dax: Add support for a dax override driver device-dax: Move resource pinning+mapping into the common driver device-dax: Introduce bus + driver model device-dax: Start defining a dax bus model device-dax: Remove multi-resource infrastructure device-dax: Kill dax_region base device-dax: Kill dax_region ida
292 lines
9.5 KiB
C
292 lines
9.5 KiB
C
/*
|
|
* libnvdimm - Non-volatile-memory Devices Subsystem
|
|
*
|
|
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
#ifndef __LIBNVDIMM_H__
|
|
#define __LIBNVDIMM_H__
|
|
#include <linux/kernel.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/types.h>
|
|
#include <linux/uuid.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
struct badrange_entry {
|
|
u64 start;
|
|
u64 length;
|
|
struct list_head list;
|
|
};
|
|
|
|
struct badrange {
|
|
struct list_head list;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
enum {
|
|
/* when a dimm supports both PMEM and BLK access a label is required */
|
|
NDD_ALIASING = 0,
|
|
/* unarmed memory devices may not persist writes */
|
|
NDD_UNARMED = 1,
|
|
/* locked memory devices should not be accessed */
|
|
NDD_LOCKED = 2,
|
|
/* memory under security wipes should not be accessed */
|
|
NDD_SECURITY_OVERWRITE = 3,
|
|
/* tracking whether or not there is a pending device reference */
|
|
NDD_WORK_PENDING = 4,
|
|
/* ignore / filter NSLABEL_FLAG_LOCAL for this DIMM, i.e. no aliasing */
|
|
NDD_NOBLK = 5,
|
|
|
|
/* 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 = 256,
|
|
ND_MAX_MAPPINGS = 32,
|
|
|
|
/* region flag indicating to direct-map persistent memory by default */
|
|
ND_REGION_PAGEMAP = 0,
|
|
/*
|
|
* Platform ensures entire CPU store data path is flushed to pmem on
|
|
* system power loss.
|
|
*/
|
|
ND_REGION_PERSIST_CACHE = 1,
|
|
/*
|
|
* Platform provides mechanisms to automatically flush outstanding
|
|
* write data from memory controler to pmem on system power loss.
|
|
* (ADR)
|
|
*/
|
|
ND_REGION_PERSIST_MEMCTRL = 2,
|
|
|
|
/* mark newly adjusted resources as requiring a label update */
|
|
DPA_RESOURCE_ADJUSTED = 1 << 0,
|
|
};
|
|
|
|
extern struct attribute_group nvdimm_bus_attribute_group;
|
|
extern struct attribute_group nvdimm_attribute_group;
|
|
extern struct attribute_group nd_device_attribute_group;
|
|
extern struct attribute_group nd_numa_attribute_group;
|
|
extern struct attribute_group nd_region_attribute_group;
|
|
extern struct attribute_group nd_mapping_attribute_group;
|
|
|
|
struct nvdimm;
|
|
struct nvdimm_bus_descriptor;
|
|
typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc,
|
|
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
|
unsigned int buf_len, int *cmd_rc);
|
|
|
|
struct device_node;
|
|
struct nvdimm_bus_descriptor {
|
|
const struct attribute_group **attr_groups;
|
|
unsigned long bus_dsm_mask;
|
|
unsigned long cmd_mask;
|
|
struct module *module;
|
|
char *provider_name;
|
|
struct device_node *of_node;
|
|
ndctl_fn ndctl;
|
|
int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
|
|
int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc,
|
|
struct nvdimm *nvdimm, unsigned int cmd, void *data);
|
|
};
|
|
|
|
struct nd_cmd_desc {
|
|
int in_num;
|
|
int out_num;
|
|
u32 in_sizes[ND_CMD_MAX_ELEM];
|
|
int out_sizes[ND_CMD_MAX_ELEM];
|
|
};
|
|
|
|
struct nd_interleave_set {
|
|
/* v1.1 definition of the interleave-set-cookie algorithm */
|
|
u64 cookie1;
|
|
/* v1.2 definition of the interleave-set-cookie algorithm */
|
|
u64 cookie2;
|
|
/* compatibility with initial buggy Linux implementation */
|
|
u64 altcookie;
|
|
|
|
guid_t type_guid;
|
|
};
|
|
|
|
struct nd_mapping_desc {
|
|
struct nvdimm *nvdimm;
|
|
u64 start;
|
|
u64 size;
|
|
int position;
|
|
};
|
|
|
|
struct nd_region_desc {
|
|
struct resource *res;
|
|
struct nd_mapping_desc *mapping;
|
|
u16 num_mappings;
|
|
const struct attribute_group **attr_groups;
|
|
struct nd_interleave_set *nd_set;
|
|
void *provider_data;
|
|
int num_lanes;
|
|
int numa_node;
|
|
int target_node;
|
|
unsigned long flags;
|
|
struct device_node *of_node;
|
|
};
|
|
|
|
struct device;
|
|
void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset,
|
|
size_t size, unsigned long flags);
|
|
static inline void __iomem *devm_nvdimm_ioremap(struct device *dev,
|
|
resource_size_t offset, size_t size)
|
|
{
|
|
return (void __iomem *) devm_nvdimm_memremap(dev, offset, size, 0);
|
|
}
|
|
|
|
struct nvdimm_bus;
|
|
struct module;
|
|
struct device;
|
|
struct nd_blk_region;
|
|
struct nd_blk_region_desc {
|
|
int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev);
|
|
int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
|
|
void *iobuf, u64 len, int rw);
|
|
struct nd_region_desc ndr_desc;
|
|
};
|
|
|
|
static inline struct nd_blk_region_desc *to_blk_region_desc(
|
|
struct nd_region_desc *ndr_desc)
|
|
{
|
|
return container_of(ndr_desc, struct nd_blk_region_desc, ndr_desc);
|
|
|
|
}
|
|
|
|
enum nvdimm_security_state {
|
|
NVDIMM_SECURITY_ERROR = -1,
|
|
NVDIMM_SECURITY_DISABLED,
|
|
NVDIMM_SECURITY_UNLOCKED,
|
|
NVDIMM_SECURITY_LOCKED,
|
|
NVDIMM_SECURITY_FROZEN,
|
|
NVDIMM_SECURITY_OVERWRITE,
|
|
};
|
|
|
|
#define NVDIMM_PASSPHRASE_LEN 32
|
|
#define NVDIMM_KEY_DESC_LEN 22
|
|
|
|
struct nvdimm_key_data {
|
|
u8 data[NVDIMM_PASSPHRASE_LEN];
|
|
};
|
|
|
|
enum nvdimm_passphrase_type {
|
|
NVDIMM_USER,
|
|
NVDIMM_MASTER,
|
|
};
|
|
|
|
struct nvdimm_security_ops {
|
|
enum nvdimm_security_state (*state)(struct nvdimm *nvdimm,
|
|
enum nvdimm_passphrase_type pass_type);
|
|
int (*freeze)(struct nvdimm *nvdimm);
|
|
int (*change_key)(struct nvdimm *nvdimm,
|
|
const struct nvdimm_key_data *old_data,
|
|
const struct nvdimm_key_data *new_data,
|
|
enum nvdimm_passphrase_type pass_type);
|
|
int (*unlock)(struct nvdimm *nvdimm,
|
|
const struct nvdimm_key_data *key_data);
|
|
int (*disable)(struct nvdimm *nvdimm,
|
|
const struct nvdimm_key_data *key_data);
|
|
int (*erase)(struct nvdimm *nvdimm,
|
|
const struct nvdimm_key_data *key_data,
|
|
enum nvdimm_passphrase_type pass_type);
|
|
int (*overwrite)(struct nvdimm *nvdimm,
|
|
const struct nvdimm_key_data *key_data);
|
|
int (*query_overwrite)(struct nvdimm *nvdimm);
|
|
};
|
|
|
|
void badrange_init(struct badrange *badrange);
|
|
int badrange_add(struct badrange *badrange, u64 addr, u64 length);
|
|
void badrange_forget(struct badrange *badrange, phys_addr_t start,
|
|
unsigned int len);
|
|
int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr,
|
|
u64 length);
|
|
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
|
|
struct nvdimm_bus_descriptor *nfit_desc);
|
|
void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus);
|
|
struct nvdimm_bus *to_nvdimm_bus(struct device *dev);
|
|
struct nvdimm_bus *nvdimm_to_bus(struct nvdimm *nvdimm);
|
|
struct nvdimm *to_nvdimm(struct device *dev);
|
|
struct nd_region *to_nd_region(struct device *dev);
|
|
struct device *nd_region_dev(struct nd_region *nd_region);
|
|
struct nd_blk_region *to_nd_blk_region(struct device *dev);
|
|
struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
|
|
struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus);
|
|
const char *nvdimm_name(struct nvdimm *nvdimm);
|
|
struct kobject *nvdimm_kobj(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 cmd_mask, int num_flush,
|
|
struct resource *flush_wpq, const char *dimm_id,
|
|
const struct nvdimm_security_ops *sec_ops);
|
|
static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus,
|
|
void *provider_data, const struct attribute_group **groups,
|
|
unsigned long flags, unsigned long cmd_mask, int num_flush,
|
|
struct resource *flush_wpq)
|
|
{
|
|
return __nvdimm_create(nvdimm_bus, provider_data, groups, flags,
|
|
cmd_mask, num_flush, flush_wpq, NULL, NULL);
|
|
}
|
|
|
|
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,
|
|
const struct nd_cmd_desc *desc, int idx, void *buf);
|
|
u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
|
|
const struct nd_cmd_desc *desc, int idx, const u32 *in_field,
|
|
const u32 *out_field, unsigned long remainder);
|
|
int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count);
|
|
struct nd_region *nvdimm_pmem_region_create(struct nvdimm_bus *nvdimm_bus,
|
|
struct nd_region_desc *ndr_desc);
|
|
struct nd_region *nvdimm_blk_region_create(struct nvdimm_bus *nvdimm_bus,
|
|
struct nd_region_desc *ndr_desc);
|
|
struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus,
|
|
struct nd_region_desc *ndr_desc);
|
|
void *nd_region_provider_data(struct nd_region *nd_region);
|
|
void *nd_blk_region_provider_data(struct nd_blk_region *ndbr);
|
|
void nd_blk_region_set_provider_data(struct nd_blk_region *ndbr, void *data);
|
|
struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr);
|
|
unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr);
|
|
unsigned int nd_region_acquire_lane(struct nd_region *nd_region);
|
|
void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane);
|
|
u64 nd_fletcher64(void *addr, size_t len, bool le);
|
|
void nvdimm_flush(struct nd_region *nd_region);
|
|
int nvdimm_has_flush(struct nd_region *nd_region);
|
|
int nvdimm_has_cache(struct nd_region *nd_region);
|
|
int nvdimm_in_overwrite(struct nvdimm *nvdimm);
|
|
|
|
static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
|
unsigned int buf_len, int *cmd_rc)
|
|
{
|
|
struct nvdimm_bus *nvdimm_bus = nvdimm_to_bus(nvdimm);
|
|
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
|
|
|
|
return nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, cmd_rc);
|
|
}
|
|
|
|
#ifdef CONFIG_ARCH_HAS_PMEM_API
|
|
#define ARCH_MEMREMAP_PMEM MEMREMAP_WB
|
|
void arch_wb_cache_pmem(void *addr, size_t size);
|
|
void arch_invalidate_pmem(void *addr, size_t size);
|
|
#else
|
|
#define ARCH_MEMREMAP_PMEM MEMREMAP_WT
|
|
static inline void arch_wb_cache_pmem(void *addr, size_t size)
|
|
{
|
|
}
|
|
static inline void arch_invalidate_pmem(void *addr, size_t size)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#endif /* __LIBNVDIMM_H__ */
|