Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235// 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);}