mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
282 lines
6.4 KiB
C
282 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2025, Mike Rapoport, Microsoft
|
|
*
|
|
* Based on e820 pmem driver:
|
|
* Copyright (c) 2015, Christoph Hellwig.
|
|
* Copyright (c) 2015, Intel Corporation.
|
|
*/
|
|
#include <linux/platform_device.h>
|
|
#include <linux/memory_hotplug.h>
|
|
#include <linux/libnvdimm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/numa.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
|
|
#include <uapi/linux/ndctl.h>
|
|
|
|
#define LABEL_AREA_SIZE SZ_128K
|
|
|
|
struct ramdax_dimm {
|
|
struct nvdimm *nvdimm;
|
|
void *label_area;
|
|
};
|
|
|
|
static void ramdax_remove(struct platform_device *pdev)
|
|
{
|
|
struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev);
|
|
|
|
nvdimm_bus_unregister(nvdimm_bus);
|
|
}
|
|
|
|
static int ramdax_register_region(struct resource *res,
|
|
struct nvdimm *nvdimm,
|
|
struct nvdimm_bus *nvdimm_bus)
|
|
{
|
|
struct nd_mapping_desc mapping;
|
|
struct nd_region_desc ndr_desc;
|
|
struct nd_interleave_set *nd_set;
|
|
int nid = phys_to_target_node(res->start);
|
|
|
|
nd_set = kzalloc_obj(*nd_set);
|
|
if (!nd_set)
|
|
return -ENOMEM;
|
|
|
|
nd_set->cookie1 = 0xcafebeefcafebeef;
|
|
nd_set->cookie2 = nd_set->cookie1;
|
|
nd_set->altcookie = nd_set->cookie1;
|
|
|
|
memset(&mapping, 0, sizeof(mapping));
|
|
mapping.nvdimm = nvdimm;
|
|
mapping.start = 0;
|
|
mapping.size = resource_size(res) - LABEL_AREA_SIZE;
|
|
|
|
memset(&ndr_desc, 0, sizeof(ndr_desc));
|
|
ndr_desc.res = res;
|
|
ndr_desc.numa_node = numa_map_to_online_node(nid);
|
|
ndr_desc.target_node = nid;
|
|
ndr_desc.num_mappings = 1;
|
|
ndr_desc.mapping = &mapping;
|
|
ndr_desc.nd_set = nd_set;
|
|
|
|
if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc))
|
|
goto err_free_nd_set;
|
|
|
|
return 0;
|
|
|
|
err_free_nd_set:
|
|
kfree(nd_set);
|
|
return -ENXIO;
|
|
}
|
|
|
|
static int ramdax_register_dimm(struct resource *res, void *data)
|
|
{
|
|
resource_size_t start = res->start;
|
|
resource_size_t size = resource_size(res);
|
|
unsigned long flags = 0, cmd_mask = 0;
|
|
struct nvdimm_bus *nvdimm_bus = data;
|
|
struct ramdax_dimm *dimm;
|
|
int err;
|
|
|
|
dimm = kzalloc_obj(*dimm);
|
|
if (!dimm)
|
|
return -ENOMEM;
|
|
|
|
dimm->label_area = memremap(start + size - LABEL_AREA_SIZE,
|
|
LABEL_AREA_SIZE, MEMREMAP_WB);
|
|
if (!dimm->label_area) {
|
|
err = -ENOMEM;
|
|
goto err_free_dimm;
|
|
}
|
|
|
|
set_bit(NDD_LABELING, &flags);
|
|
set_bit(NDD_REGISTER_SYNC, &flags);
|
|
set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
|
|
set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
|
|
set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
|
|
dimm->nvdimm = nvdimm_create(nvdimm_bus, dimm,
|
|
/* dimm_attribute_groups */ NULL,
|
|
flags, cmd_mask, 0, NULL);
|
|
if (!dimm->nvdimm) {
|
|
err = -ENOMEM;
|
|
goto err_unmap_label;
|
|
}
|
|
|
|
err = ramdax_register_region(res, dimm->nvdimm, nvdimm_bus);
|
|
if (err)
|
|
goto err_remove_nvdimm;
|
|
|
|
return 0;
|
|
|
|
err_remove_nvdimm:
|
|
nvdimm_delete(dimm->nvdimm);
|
|
err_unmap_label:
|
|
memunmap(dimm->label_area);
|
|
err_free_dimm:
|
|
kfree(dimm);
|
|
return err;
|
|
}
|
|
|
|
static int ramdax_get_config_size(struct nvdimm *nvdimm, int buf_len,
|
|
struct nd_cmd_get_config_size *cmd)
|
|
{
|
|
if (sizeof(*cmd) > buf_len)
|
|
return -EINVAL;
|
|
|
|
*cmd = (struct nd_cmd_get_config_size){
|
|
.status = 0,
|
|
.config_size = LABEL_AREA_SIZE,
|
|
.max_xfer = 8,
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ramdax_get_config_data(struct nvdimm *nvdimm, int buf_len,
|
|
struct nd_cmd_get_config_data_hdr *cmd)
|
|
{
|
|
struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
|
|
|
|
if (sizeof(*cmd) > buf_len)
|
|
return -EINVAL;
|
|
if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
|
|
return -EINVAL;
|
|
if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
|
|
return -EINVAL;
|
|
|
|
memcpy(cmd->out_buf, dimm->label_area + cmd->in_offset, cmd->in_length);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ramdax_set_config_data(struct nvdimm *nvdimm, int buf_len,
|
|
struct nd_cmd_set_config_hdr *cmd)
|
|
{
|
|
struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
|
|
|
|
if (sizeof(*cmd) > buf_len)
|
|
return -EINVAL;
|
|
if (struct_size(cmd, in_buf, cmd->in_length) > buf_len)
|
|
return -EINVAL;
|
|
if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
|
|
return -EINVAL;
|
|
|
|
memcpy(dimm->label_area + cmd->in_offset, cmd->in_buf, cmd->in_length);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ramdax_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
|
|
void *buf, unsigned int buf_len)
|
|
{
|
|
unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
|
|
|
|
if (!test_bit(cmd, &cmd_mask))
|
|
return -ENOTTY;
|
|
|
|
switch (cmd) {
|
|
case ND_CMD_GET_CONFIG_SIZE:
|
|
return ramdax_get_config_size(nvdimm, buf_len, buf);
|
|
case ND_CMD_GET_CONFIG_DATA:
|
|
return ramdax_get_config_data(nvdimm, buf_len, buf);
|
|
case ND_CMD_SET_CONFIG_DATA:
|
|
return ramdax_set_config_data(nvdimm, buf_len, buf);
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
static int ramdax_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
|
unsigned int buf_len, int *cmd_rc)
|
|
{
|
|
/*
|
|
* No firmware response to translate, let the transport error
|
|
* code take precedence.
|
|
*/
|
|
*cmd_rc = 0;
|
|
|
|
if (!nvdimm)
|
|
return -ENOTTY;
|
|
return ramdax_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id ramdax_of_matches[] = {
|
|
{ .compatible = "pmem-region", },
|
|
{ },
|
|
};
|
|
#endif
|
|
|
|
static int ramdax_probe_of(struct platform_device *pdev,
|
|
struct nvdimm_bus *bus, struct device_node *np)
|
|
{
|
|
int err;
|
|
|
|
if (!of_match_node(ramdax_of_matches, np))
|
|
return -ENODEV;
|
|
|
|
for (int i = 0; i < pdev->num_resources; i++) {
|
|
err = ramdax_register_dimm(&pdev->resource[i], bus);
|
|
if (err)
|
|
goto err_unregister;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_unregister:
|
|
/*
|
|
* FIXME: should we unregister the dimms that were registered
|
|
* successfully
|
|
*/
|
|
return err;
|
|
}
|
|
|
|
static int ramdax_probe(struct platform_device *pdev)
|
|
{
|
|
static struct nvdimm_bus_descriptor nd_desc;
|
|
struct device *dev = &pdev->dev;
|
|
struct nvdimm_bus *nvdimm_bus;
|
|
struct device_node *np;
|
|
int rc = -ENXIO;
|
|
|
|
nd_desc.provider_name = "ramdax";
|
|
nd_desc.module = THIS_MODULE;
|
|
nd_desc.ndctl = ramdax_ctl;
|
|
nvdimm_bus = nvdimm_bus_register(dev, &nd_desc);
|
|
if (!nvdimm_bus)
|
|
goto err;
|
|
|
|
np = dev_of_node(&pdev->dev);
|
|
if (np)
|
|
rc = ramdax_probe_of(pdev, nvdimm_bus, np);
|
|
else
|
|
rc = walk_iomem_res_desc(IORES_DESC_PERSISTENT_MEMORY_LEGACY,
|
|
IORESOURCE_MEM, 0, -1, nvdimm_bus,
|
|
ramdax_register_dimm);
|
|
if (rc)
|
|
goto err;
|
|
|
|
platform_set_drvdata(pdev, nvdimm_bus);
|
|
|
|
return 0;
|
|
err:
|
|
nvdimm_bus_unregister(nvdimm_bus);
|
|
return rc;
|
|
}
|
|
|
|
static struct platform_driver ramdax_driver = {
|
|
.probe = ramdax_probe,
|
|
.remove = ramdax_remove,
|
|
.driver = {
|
|
.name = "ramdax",
|
|
},
|
|
};
|
|
|
|
module_platform_driver(ramdax_driver);
|
|
|
|
MODULE_DESCRIPTION("NVDIMM support for e820 type-12 memory and OF pmem-region");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Microsoft Corporation");
|