// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2001-2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ #include "dm.h"#include <linux/module.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/bio.h>#include <linux/dax.h>#include <linux/slab.h>#include <linux/device-mapper.h> #define DM_MSG_PREFIX "linear" /* * Linear: maps a linear range of a device. */struct linear_c { struct dm_dev *dev; sector_t start;}; /* * Construct a linear mapping: <dev_path> <offset> */static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv){ struct linear_c *lc; unsigned long long tmp; char dummy; int ret; if (argc != 2) { ti->error = "Invalid argument count"; return -EINVAL; } lc = kmalloc_obj(*lc); if (lc == NULL) { ti->error = "Cannot allocate linear context"; return -ENOMEM; } ret = -EINVAL; if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 || tmp != (sector_t)tmp) { ti->error = "Invalid device sector"; goto bad; } lc->start = tmp; ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev); if (ret) { ti->error = "Device lookup failed"; goto bad; } ti->num_flush_bios = 1; ti->num_discard_bios = 1; ti->num_secure_erase_bios = 1; ti->num_write_zeroes_bios = 1; ti->flush_bypasses_map = true; ti->private = lc; return 0; bad: kfree(lc); return ret;} static void linear_dtr(struct dm_target *ti){ struct linear_c *lc = ti->private; dm_put_device(ti, lc->dev); kfree(lc);} static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector){ struct linear_c *lc = ti->private; return lc->start + dm_target_offset(ti, bi_sector);} int linear_map(struct dm_target *ti, struct bio *bio){ struct linear_c *lc = ti->private; bio_set_dev(bio, lc->dev->bdev); bio->bi_iter.bi_sector = linear_map_sector(ti, bio->bi_iter.bi_sector); return DM_MAPIO_REMAPPED;} static void linear_status(struct dm_target *ti, status_type_t type, unsigned int status_flags, char *result, unsigned int maxlen){ struct linear_c *lc = ti->private; size_t sz = 0; switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; case STATUSTYPE_TABLE: DMEMIT("%s %llu", lc->dev->name, (unsigned long long)lc->start); break; case STATUSTYPE_IMA: DMEMIT_TARGET_NAME_VERSION(ti->type); DMEMIT(",device_name=%s,start=%llu;", lc->dev->name, (unsigned long long)lc->start); break; }} static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev, unsigned int cmd, unsigned long arg, bool *forward){ struct linear_c *lc = ti->private; struct dm_dev *dev = lc->dev; *bdev = dev->bdev; /* * Only pass ioctls through if the device sizes match exactly. */ if (lc->start || ti->len != bdev_nr_sectors(dev->bdev)) return 1; return 0;} #ifdef CONFIG_BLK_DEV_ZONEDstatic int linear_report_zones(struct dm_target *ti, struct dm_report_zones_args *args, unsigned int nr_zones){ struct linear_c *lc = ti->private; return dm_report_zones(lc->dev->bdev, lc->start, linear_map_sector(ti, args->next_sector), args, nr_zones);}#else#define linear_report_zones NULL#endif static int linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data){ struct linear_c *lc = ti->private; return fn(ti, lc->dev, lc->start, ti->len, data);} #if IS_ENABLED(CONFIG_FS_DAX)static struct dax_device *linear_dax_pgoff(struct dm_target *ti, pgoff_t *pgoff){ struct linear_c *lc = ti->private; sector_t sector = linear_map_sector(ti, *pgoff << PAGE_SECTORS_SHIFT); *pgoff = (get_start_sect(lc->dev->bdev) + sector) >> PAGE_SECTORS_SHIFT; return lc->dev->dax_dev;} static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, long nr_pages, enum dax_access_mode mode, void **kaddr, unsigned long *pfn){ struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff); return dax_direct_access(dax_dev, pgoff, nr_pages, mode, kaddr, pfn);} static int linear_dax_zero_page_range(struct dm_target *ti, pgoff_t pgoff, size_t nr_pages){ struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff); return dax_zero_page_range(dax_dev, pgoff, nr_pages);} static size_t linear_dax_recovery_write(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i){ struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff); return dax_recovery_write(dax_dev, pgoff, addr, bytes, i);} #else#define linear_dax_direct_access NULL#define linear_dax_zero_page_range NULL#define linear_dax_recovery_write NULL#endif static struct target_type linear_target = { .name = "linear", .version = {1, 5, 0}, .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT | DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO | DM_TARGET_ATOMIC_WRITES, .report_zones = linear_report_zones, .module = THIS_MODULE, .ctr = linear_ctr, .dtr = linear_dtr, .map = linear_map, .status = linear_status, .prepare_ioctl = linear_prepare_ioctl, .iterate_devices = linear_iterate_devices, .direct_access = linear_dax_direct_access, .dax_zero_page_range = linear_dax_zero_page_range, .dax_recovery_write = linear_dax_recovery_write,}; int __init dm_linear_init(void){ int r = dm_register_target(&linear_target); if (r < 0) DMERR("register failed %d", r); return r;} void dm_linear_exit(void){ dm_unregister_target(&linear_target);}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2001-2003 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#include "dm.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/dax.h>
#include <linux/slab.h>
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "linear"
/*
* Linear: maps a linear range of a device.
*/
struct linear_c {
struct dm_dev *dev;
sector_t start;
};
/*
* Construct a linear mapping: <dev_path> <offset>
*/
static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct linear_c *lc;
unsigned long long tmp;
char dummy;
int ret;
if (argc != 2) {
ti->error = "Invalid argument count";
return -EINVAL;
}
lc = kmalloc_obj(*lc);
if (lc == NULL) {
ti->error = "Cannot allocate linear context";
return -ENOMEM;
}
ret = -EINVAL;
if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 || tmp != (sector_t)tmp) {
ti->error = "Invalid device sector";
goto bad;
}
lc->start = tmp;
ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev);
if (ret) {
ti->error = "Device lookup failed";
goto bad;
}
ti->num_flush_bios = 1;
ti->num_discard_bios = 1;
ti->num_secure_erase_bios = 1;
ti->num_write_zeroes_bios = 1;
ti->flush_bypasses_map = true;
ti->private = lc;
return 0;
bad:
kfree(lc);
return ret;
}
static void linear_dtr(struct dm_target *ti)
{
struct linear_c *lc = ti->private;
dm_put_device(ti, lc->dev);
kfree(lc);
}
static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector)
{
struct linear_c *lc = ti->private;
return lc->start + dm_target_offset(ti, bi_sector);
}
int linear_map(struct dm_target *ti, struct bio *bio)
{
struct linear_c *lc = ti->private;
bio_set_dev(bio, lc->dev->bdev);
bio->bi_iter.bi_sector = linear_map_sector(ti, bio->bi_iter.bi_sector);
return DM_MAPIO_REMAPPED;
}
static void linear_status(struct dm_target *ti, status_type_t type,
unsigned int status_flags, char *result, unsigned int maxlen)
{
struct linear_c *lc = ti->private;
size_t sz = 0;
switch (type) {
case STATUSTYPE_INFO:
result[0] = '\0';
break;
case STATUSTYPE_TABLE:
DMEMIT("%s %llu", lc->dev->name, (unsigned long long)lc->start);
break;
case STATUSTYPE_IMA:
DMEMIT_TARGET_NAME_VERSION(ti->type);
DMEMIT(",device_name=%s,start=%llu;", lc->dev->name,
(unsigned long long)lc->start);
break;
}
}
static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct linear_c *lc = ti->private;
struct dm_dev *dev = lc->dev;
*bdev = dev->bdev;
/*
* Only pass ioctls through if the device sizes match exactly.
*/
if (lc->start || ti->len != bdev_nr_sectors(dev->bdev))
return 1;
return 0;
}
#ifdef CONFIG_BLK_DEV_ZONED
static int linear_report_zones(struct dm_target *ti,
struct dm_report_zones_args *args, unsigned int nr_zones)
{
struct linear_c *lc = ti->private;
return dm_report_zones(lc->dev->bdev, lc->start,
linear_map_sector(ti, args->next_sector),
args, nr_zones);
}
#else
#define linear_report_zones NULL
#endif
static int linear_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct linear_c *lc = ti->private;
return fn(ti, lc->dev, lc->start, ti->len, data);
}
#if IS_ENABLED(CONFIG_FS_DAX)
static struct dax_device *linear_dax_pgoff(struct dm_target *ti, pgoff_t *pgoff)
{
struct linear_c *lc = ti->private;
sector_t sector = linear_map_sector(ti, *pgoff << PAGE_SECTORS_SHIFT);
*pgoff = (get_start_sect(lc->dev->bdev) + sector) >> PAGE_SECTORS_SHIFT;
return lc->dev->dax_dev;
}
static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
long nr_pages, enum dax_access_mode mode, void **kaddr,
unsigned long *pfn)
{
struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
return dax_direct_access(dax_dev, pgoff, nr_pages, mode, kaddr, pfn);
}
static int linear_dax_zero_page_range(struct dm_target *ti, pgoff_t pgoff,
size_t nr_pages)
{
struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
return dax_zero_page_range(dax_dev, pgoff, nr_pages);
}
static size_t linear_dax_recovery_write(struct dm_target *ti, pgoff_t pgoff,
void *addr, size_t bytes, struct iov_iter *i)
{
struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
return dax_recovery_write(dax_dev, pgoff, addr, bytes, i);
}
#else
#define linear_dax_direct_access NULL
#define linear_dax_zero_page_range NULL
#define linear_dax_recovery_write NULL
#endif
static struct target_type linear_target = {
.name = "linear",
.version = {1, 5, 0},
.features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT |
DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO |
DM_TARGET_ATOMIC_WRITES,
.report_zones = linear_report_zones,
.module = THIS_MODULE,
.ctr = linear_ctr,
.dtr = linear_dtr,
.map = linear_map,
.status = linear_status,
.prepare_ioctl = linear_prepare_ioctl,
.iterate_devices = linear_iterate_devices,
.direct_access = linear_dax_direct_access,
.dax_zero_page_range = linear_dax_zero_page_range,
.dax_recovery_write = linear_dax_recovery_write,
};
int __init dm_linear_init(void)
{
int r = dm_register_target(&linear_target);
if (r < 0)
DMERR("register failed %d", r);
return r;
}
void dm_linear_exit(void)
{
dm_unregister_target(&linear_target);
}