13bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Copyright (C) 2001-2003 Sistina Software (UK) Limited.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * This file is released under the GPL.
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #include "dm.h"
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds #include <linux/blkdev.h>
121da177e4SLinus Torvalds #include <linux/bio.h>
13817bf402SDan Williams #include <linux/dax.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
15586e80e6SMikulas Patocka #include <linux/device-mapper.h>
161da177e4SLinus Torvalds
1772d94861SAlasdair G Kergon #define DM_MSG_PREFIX "linear"
1872d94861SAlasdair G Kergon
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds * Linear: maps a linear range of a device.
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds struct linear_c {
231da177e4SLinus Torvalds struct dm_dev *dev;
241da177e4SLinus Torvalds sector_t start;
251da177e4SLinus Torvalds };
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds * Construct a linear mapping: <dev_path> <offset>
291da177e4SLinus Torvalds */
linear_ctr(struct dm_target * ti,unsigned int argc,char ** argv)301da177e4SLinus Torvalds static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
311da177e4SLinus Torvalds {
321da177e4SLinus Torvalds struct linear_c *lc;
334ee218cdSAndrew Morton unsigned long long tmp;
3431998ef1SMikulas Patocka char dummy;
35e80d1c80SVivek Goyal int ret;
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds if (argc != 2) {
3872d94861SAlasdair G Kergon ti->error = "Invalid argument count";
391da177e4SLinus Torvalds return -EINVAL;
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds lc = kmalloc(sizeof(*lc), GFP_KERNEL);
431da177e4SLinus Torvalds if (lc == NULL) {
4400272c85STomohiro Kusumi ti->error = "Cannot allocate linear context";
451da177e4SLinus Torvalds return -ENOMEM;
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds
48e80d1c80SVivek Goyal ret = -EINVAL;
49ef87bfc2SMilan Broz if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 || tmp != (sector_t)tmp) {
5000272c85STomohiro Kusumi ti->error = "Invalid device sector";
511da177e4SLinus Torvalds goto bad;
521da177e4SLinus Torvalds }
534ee218cdSAndrew Morton lc->start = tmp;
541da177e4SLinus Torvalds
55e80d1c80SVivek Goyal ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev);
56e80d1c80SVivek Goyal if (ret) {
5700272c85STomohiro Kusumi ti->error = "Device lookup failed";
581da177e4SLinus Torvalds goto bad;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds
6155a62eefSAlasdair G Kergon ti->num_flush_bios = 1;
6255a62eefSAlasdair G Kergon ti->num_discard_bios = 1;
6300716545SDenis Semakin ti->num_secure_erase_bios = 1;
64ac62d620SChristoph Hellwig ti->num_write_zeroes_bios = 1;
651da177e4SLinus Torvalds ti->private = lc;
661da177e4SLinus Torvalds return 0;
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds bad:
691da177e4SLinus Torvalds kfree(lc);
70e80d1c80SVivek Goyal return ret;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds
linear_dtr(struct dm_target * ti)731da177e4SLinus Torvalds static void linear_dtr(struct dm_target *ti)
741da177e4SLinus Torvalds {
75*26cb62a2SYu Zhe struct linear_c *lc = ti->private;
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds dm_put_device(ti, lc->dev);
781da177e4SLinus Torvalds kfree(lc);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds
linear_map_sector(struct dm_target * ti,sector_t bi_sector)817bc3447bSMilan Broz static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector)
827bc3447bSMilan Broz {
837bc3447bSMilan Broz struct linear_c *lc = ti->private;
847bc3447bSMilan Broz
85b441a262SAlasdair G Kergon return lc->start + dm_target_offset(ti, bi_sector);
867bc3447bSMilan Broz }
877bc3447bSMilan Broz
linear_map(struct dm_target * ti,struct bio * bio)88e86f2b00SMike Snitzer static int linear_map(struct dm_target *ti, struct bio *bio)
897bc3447bSMilan Broz {
907bc3447bSMilan Broz struct linear_c *lc = ti->private;
917bc3447bSMilan Broz
9274d46992SChristoph Hellwig bio_set_dev(bio, lc->dev->bdev);
93e86f2b00SMike Snitzer bio->bi_iter.bi_sector = linear_map_sector(ti, bio->bi_iter.bi_sector);
941da177e4SLinus Torvalds
95d2a7ad29SKiyoshi Ueda return DM_MAPIO_REMAPPED;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
linear_status(struct dm_target * ti,status_type_t type,unsigned int status_flags,char * result,unsigned int maxlen)98fd7c092eSMikulas Patocka static void linear_status(struct dm_target *ti, status_type_t type,
9986a3238cSHeinz Mauelshagen unsigned int status_flags, char *result, unsigned int maxlen)
1001da177e4SLinus Torvalds {
101*26cb62a2SYu Zhe struct linear_c *lc = ti->private;
1028ec45662STushar Sugandhi size_t sz = 0;
1031da177e4SLinus Torvalds
1041da177e4SLinus Torvalds switch (type) {
1051da177e4SLinus Torvalds case STATUSTYPE_INFO:
1061da177e4SLinus Torvalds result[0] = '\0';
1071da177e4SLinus Torvalds break;
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds case STATUSTYPE_TABLE:
1108ec45662STushar Sugandhi DMEMIT("%s %llu", lc->dev->name, (unsigned long long)lc->start);
1118ec45662STushar Sugandhi break;
1128ec45662STushar Sugandhi
1138ec45662STushar Sugandhi case STATUSTYPE_IMA:
1148ec45662STushar Sugandhi DMEMIT_TARGET_NAME_VERSION(ti->type);
1158ec45662STushar Sugandhi DMEMIT(",device_name=%s,start=%llu;", lc->dev->name,
1164ee218cdSAndrew Morton (unsigned long long)lc->start);
1171da177e4SLinus Torvalds break;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
linear_prepare_ioctl(struct dm_target * ti,struct block_device ** bdev)1215bd5e8d8SMike Snitzer static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
122ab17ffa4SMilan Broz {
123*26cb62a2SYu Zhe struct linear_c *lc = ti->private;
124ec8013beSPaolo Bonzini struct dm_dev *dev = lc->dev;
125e56f81e0SChristoph Hellwig
126e56f81e0SChristoph Hellwig *bdev = dev->bdev;
127ec8013beSPaolo Bonzini
128ec8013beSPaolo Bonzini /*
129ec8013beSPaolo Bonzini * Only pass ioctls through if the device sizes match exactly.
130ec8013beSPaolo Bonzini */
1316dcbb52cSChristoph Hellwig if (lc->start || ti->len != bdev_nr_sectors(dev->bdev))
132e56f81e0SChristoph Hellwig return 1;
133e56f81e0SChristoph Hellwig return 0;
134ab17ffa4SMilan Broz }
135ab17ffa4SMilan Broz
136e76239a3SChristoph Hellwig #ifdef CONFIG_BLK_DEV_ZONED
linear_report_zones(struct dm_target * ti,struct dm_report_zones_args * args,unsigned int nr_zones)137d4100351SChristoph Hellwig static int linear_report_zones(struct dm_target *ti,
138d4100351SChristoph Hellwig struct dm_report_zones_args *args, unsigned int nr_zones)
139e76239a3SChristoph Hellwig {
140d4100351SChristoph Hellwig struct linear_c *lc = ti->private;
141e76239a3SChristoph Hellwig
142912e8875SDamien Le Moal return dm_report_zones(lc->dev->bdev, lc->start,
143912e8875SDamien Le Moal linear_map_sector(ti, args->next_sector),
144912e8875SDamien Le Moal args, nr_zones);
145e76239a3SChristoph Hellwig }
146e3290b94SMike Snitzer #else
147e3290b94SMike Snitzer #define linear_report_zones NULL
148e76239a3SChristoph Hellwig #endif
149e76239a3SChristoph Hellwig
linear_iterate_devices(struct dm_target * ti,iterate_devices_callout_fn fn,void * data)150af4874e0SMike Snitzer static int linear_iterate_devices(struct dm_target *ti,
151af4874e0SMike Snitzer iterate_devices_callout_fn fn, void *data)
152af4874e0SMike Snitzer {
153af4874e0SMike Snitzer struct linear_c *lc = ti->private;
154af4874e0SMike Snitzer
1555dea271bSMike Snitzer return fn(ti, lc->dev, lc->start, ti->len, data);
156af4874e0SMike Snitzer }
157af4874e0SMike Snitzer
1585d2a228bSChristoph Hellwig #if IS_ENABLED(CONFIG_FS_DAX)
linear_dax_pgoff(struct dm_target * ti,pgoff_t * pgoff)159f43e0065SChristoph Hellwig static struct dax_device *linear_dax_pgoff(struct dm_target *ti, pgoff_t *pgoff)
160f43e0065SChristoph Hellwig {
161f43e0065SChristoph Hellwig struct linear_c *lc = ti->private;
162f43e0065SChristoph Hellwig sector_t sector = linear_map_sector(ti, *pgoff << PAGE_SECTORS_SHIFT);
163f43e0065SChristoph Hellwig
164f43e0065SChristoph Hellwig *pgoff = (get_start_sect(lc->dev->bdev) + sector) >> PAGE_SECTORS_SHIFT;
165f43e0065SChristoph Hellwig return lc->dev->dax_dev;
166f43e0065SChristoph Hellwig }
167f43e0065SChristoph Hellwig
linear_dax_direct_access(struct dm_target * ti,pgoff_t pgoff,long nr_pages,enum dax_access_mode mode,void ** kaddr,pfn_t * pfn)168817bf402SDan Williams static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
169e511c4a3SJane Chu long nr_pages, enum dax_access_mode mode, void **kaddr,
170e511c4a3SJane Chu pfn_t *pfn)
17184b22f83SToshi Kani {
172f43e0065SChristoph Hellwig struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
17384b22f83SToshi Kani
174e511c4a3SJane Chu return dax_direct_access(dax_dev, pgoff, nr_pages, mode, kaddr, pfn);
17584b22f83SToshi Kani }
17684b22f83SToshi Kani
linear_dax_zero_page_range(struct dm_target * ti,pgoff_t pgoff,size_t nr_pages)177cdf6cdcdSVivek Goyal static int linear_dax_zero_page_range(struct dm_target *ti, pgoff_t pgoff,
178cdf6cdcdSVivek Goyal size_t nr_pages)
179cdf6cdcdSVivek Goyal {
180f43e0065SChristoph Hellwig struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
181cdf6cdcdSVivek Goyal
182cdf6cdcdSVivek Goyal return dax_zero_page_range(dax_dev, pgoff, nr_pages);
183cdf6cdcdSVivek Goyal }
184cdf6cdcdSVivek Goyal
linear_dax_recovery_write(struct dm_target * ti,pgoff_t pgoff,void * addr,size_t bytes,struct iov_iter * i)185047218ecSJane Chu static size_t linear_dax_recovery_write(struct dm_target *ti, pgoff_t pgoff,
186047218ecSJane Chu void *addr, size_t bytes, struct iov_iter *i)
187047218ecSJane Chu {
188047218ecSJane Chu struct dax_device *dax_dev = linear_dax_pgoff(ti, &pgoff);
189047218ecSJane Chu
190047218ecSJane Chu return dax_recovery_write(dax_dev, pgoff, addr, bytes, i);
191047218ecSJane Chu }
192047218ecSJane Chu
193976431b0SDan Williams #else
194976431b0SDan Williams #define linear_dax_direct_access NULL
195cdf6cdcdSVivek Goyal #define linear_dax_zero_page_range NULL
196047218ecSJane Chu #define linear_dax_recovery_write NULL
197976431b0SDan Williams #endif
198976431b0SDan Williams
1991da177e4SLinus Torvalds static struct target_type linear_target = {
2001da177e4SLinus Torvalds .name = "linear",
2010be12c1cSDamien Le Moal .version = {1, 4, 0},
2026abc4946SKonstantin Khlebnikov .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT |
2033db564b4SSatya Tangirala DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,
204e76239a3SChristoph Hellwig .report_zones = linear_report_zones,
2051da177e4SLinus Torvalds .module = THIS_MODULE,
2061da177e4SLinus Torvalds .ctr = linear_ctr,
2071da177e4SLinus Torvalds .dtr = linear_dtr,
2081da177e4SLinus Torvalds .map = linear_map,
2091da177e4SLinus Torvalds .status = linear_status,
210e56f81e0SChristoph Hellwig .prepare_ioctl = linear_prepare_ioctl,
211af4874e0SMike Snitzer .iterate_devices = linear_iterate_devices,
212817bf402SDan Williams .direct_access = linear_dax_direct_access,
213cdf6cdcdSVivek Goyal .dax_zero_page_range = linear_dax_zero_page_range,
214047218ecSJane Chu .dax_recovery_write = linear_dax_recovery_write,
2151da177e4SLinus Torvalds };
2161da177e4SLinus Torvalds
dm_linear_init(void)2171da177e4SLinus Torvalds int __init dm_linear_init(void)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds int r = dm_register_target(&linear_target);
2201da177e4SLinus Torvalds
2211da177e4SLinus Torvalds if (r < 0)
22272d94861SAlasdair G Kergon DMERR("register failed %d", r);
2231da177e4SLinus Torvalds
2241da177e4SLinus Torvalds return r;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds
dm_linear_exit(void)2271da177e4SLinus Torvalds void dm_linear_exit(void)
2281da177e4SLinus Torvalds {
22910d3bd09SMikulas Patocka dm_unregister_target(&linear_target);
2301da177e4SLinus Torvalds }
231