1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2001-2003 Sistina Software (UK) Limited. |
4 | * |
5 | * This file is released under the GPL. |
6 | */ |
7 | |
8 | #include "dm.h" |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/blkdev.h> |
12 | #include <linux/bio.h> |
13 | #include <linux/dax.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/device-mapper.h> |
16 | |
17 | #define DM_MSG_PREFIX "linear" |
18 | |
19 | /* |
20 | * Linear: maps a linear range of a device. |
21 | */ |
22 | struct linear_c { |
23 | struct dm_dev *dev; |
24 | sector_t start; |
25 | }; |
26 | |
27 | /* |
28 | * Construct a linear mapping: <dev_path> <offset> |
29 | */ |
30 | static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
31 | { |
32 | struct linear_c *lc; |
33 | unsigned long long tmp; |
34 | char dummy; |
35 | int ret; |
36 | |
37 | if (argc != 2) { |
38 | ti->error = "Invalid argument count" ; |
39 | return -EINVAL; |
40 | } |
41 | |
42 | lc = kmalloc(size: sizeof(*lc), GFP_KERNEL); |
43 | if (lc == NULL) { |
44 | ti->error = "Cannot allocate linear context" ; |
45 | return -ENOMEM; |
46 | } |
47 | |
48 | ret = -EINVAL; |
49 | if (sscanf(argv[1], "%llu%c" , &tmp, &dummy) != 1 || tmp != (sector_t)tmp) { |
50 | ti->error = "Invalid device sector" ; |
51 | goto bad; |
52 | } |
53 | lc->start = tmp; |
54 | |
55 | ret = dm_get_device(ti, path: argv[0], mode: dm_table_get_mode(t: ti->table), result: &lc->dev); |
56 | if (ret) { |
57 | ti->error = "Device lookup failed" ; |
58 | goto bad; |
59 | } |
60 | |
61 | ti->num_flush_bios = 1; |
62 | ti->num_discard_bios = 1; |
63 | ti->num_secure_erase_bios = 1; |
64 | ti->num_write_zeroes_bios = 1; |
65 | ti->private = lc; |
66 | return 0; |
67 | |
68 | bad: |
69 | kfree(objp: lc); |
70 | return ret; |
71 | } |
72 | |
73 | static void linear_dtr(struct dm_target *ti) |
74 | { |
75 | struct linear_c *lc = ti->private; |
76 | |
77 | dm_put_device(ti, d: lc->dev); |
78 | kfree(objp: lc); |
79 | } |
80 | |
81 | static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector) |
82 | { |
83 | struct linear_c *lc = ti->private; |
84 | |
85 | return lc->start + dm_target_offset(ti, bi_sector); |
86 | } |
87 | |
88 | int linear_map(struct dm_target *ti, struct bio *bio) |
89 | { |
90 | struct linear_c *lc = ti->private; |
91 | |
92 | bio_set_dev(bio, bdev: lc->dev->bdev); |
93 | bio->bi_iter.bi_sector = linear_map_sector(ti, bi_sector: bio->bi_iter.bi_sector); |
94 | |
95 | return DM_MAPIO_REMAPPED; |
96 | } |
97 | |
98 | static void linear_status(struct dm_target *ti, status_type_t type, |
99 | unsigned int status_flags, char *result, unsigned int maxlen) |
100 | { |
101 | struct linear_c *lc = ti->private; |
102 | size_t sz = 0; |
103 | |
104 | switch (type) { |
105 | case STATUSTYPE_INFO: |
106 | result[0] = '\0'; |
107 | break; |
108 | |
109 | case STATUSTYPE_TABLE: |
110 | DMEMIT("%s %llu" , lc->dev->name, (unsigned long long)lc->start); |
111 | break; |
112 | |
113 | case STATUSTYPE_IMA: |
114 | DMEMIT_TARGET_NAME_VERSION(ti->type); |
115 | DMEMIT(",device_name=%s,start=%llu;" , lc->dev->name, |
116 | (unsigned long long)lc->start); |
117 | break; |
118 | } |
119 | } |
120 | |
121 | static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) |
122 | { |
123 | struct linear_c *lc = ti->private; |
124 | struct dm_dev *dev = lc->dev; |
125 | |
126 | *bdev = dev->bdev; |
127 | |
128 | /* |
129 | * Only pass ioctls through if the device sizes match exactly. |
130 | */ |
131 | if (lc->start || ti->len != bdev_nr_sectors(bdev: dev->bdev)) |
132 | return 1; |
133 | return 0; |
134 | } |
135 | |
136 | #ifdef CONFIG_BLK_DEV_ZONED |
137 | static int linear_report_zones(struct dm_target *ti, |
138 | struct dm_report_zones_args *args, unsigned int nr_zones) |
139 | { |
140 | struct linear_c *lc = ti->private; |
141 | |
142 | return dm_report_zones(bdev: lc->dev->bdev, start: lc->start, |
143 | sector: linear_map_sector(ti, bi_sector: args->next_sector), |
144 | args, nr_zones); |
145 | } |
146 | #else |
147 | #define linear_report_zones NULL |
148 | #endif |
149 | |
150 | static int linear_iterate_devices(struct dm_target *ti, |
151 | iterate_devices_callout_fn fn, void *data) |
152 | { |
153 | struct linear_c *lc = ti->private; |
154 | |
155 | return fn(ti, lc->dev, lc->start, ti->len, data); |
156 | } |
157 | |
158 | #if IS_ENABLED(CONFIG_FS_DAX) |
159 | static struct dax_device *linear_dax_pgoff(struct dm_target *ti, pgoff_t *pgoff) |
160 | { |
161 | struct linear_c *lc = ti->private; |
162 | sector_t sector = linear_map_sector(ti, bi_sector: *pgoff << PAGE_SECTORS_SHIFT); |
163 | |
164 | *pgoff = (get_start_sect(bdev: lc->dev->bdev) + sector) >> PAGE_SECTORS_SHIFT; |
165 | return lc->dev->dax_dev; |
166 | } |
167 | |
168 | static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, |
169 | long nr_pages, enum dax_access_mode mode, void **kaddr, |
170 | pfn_t *pfn) |
171 | { |
172 | struct dax_device *dax_dev = linear_dax_pgoff(ti, pgoff: &pgoff); |
173 | |
174 | return dax_direct_access(dax_dev, pgoff, nr_pages, mode, kaddr, pfn); |
175 | } |
176 | |
177 | static int linear_dax_zero_page_range(struct dm_target *ti, pgoff_t pgoff, |
178 | size_t nr_pages) |
179 | { |
180 | struct dax_device *dax_dev = linear_dax_pgoff(ti, pgoff: &pgoff); |
181 | |
182 | return dax_zero_page_range(dax_dev, pgoff, nr_pages); |
183 | } |
184 | |
185 | static size_t linear_dax_recovery_write(struct dm_target *ti, pgoff_t pgoff, |
186 | void *addr, size_t bytes, struct iov_iter *i) |
187 | { |
188 | struct dax_device *dax_dev = linear_dax_pgoff(ti, pgoff: &pgoff); |
189 | |
190 | return dax_recovery_write(dax_dev, pgoff, addr, bytes, i); |
191 | } |
192 | |
193 | #else |
194 | #define linear_dax_direct_access NULL |
195 | #define linear_dax_zero_page_range NULL |
196 | #define linear_dax_recovery_write NULL |
197 | #endif |
198 | |
199 | static struct target_type linear_target = { |
200 | .name = "linear" , |
201 | .version = {1, 4, 0}, |
202 | .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT | |
203 | DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO, |
204 | .report_zones = linear_report_zones, |
205 | .module = THIS_MODULE, |
206 | .ctr = linear_ctr, |
207 | .dtr = linear_dtr, |
208 | .map = linear_map, |
209 | .status = linear_status, |
210 | .prepare_ioctl = linear_prepare_ioctl, |
211 | .iterate_devices = linear_iterate_devices, |
212 | .direct_access = linear_dax_direct_access, |
213 | .dax_zero_page_range = linear_dax_zero_page_range, |
214 | .dax_recovery_write = linear_dax_recovery_write, |
215 | }; |
216 | |
217 | int __init dm_linear_init(void) |
218 | { |
219 | int r = dm_register_target(t: &linear_target); |
220 | |
221 | if (r < 0) |
222 | DMERR("register failed %d" , r); |
223 | |
224 | return r; |
225 | } |
226 | |
227 | void dm_linear_exit(void) |
228 | { |
229 | dm_unregister_target(t: &linear_target); |
230 | } |
231 | |