234 lines · 5.3 KB · C
RawDownload
// 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);}