blob: 05d76f337838ad662ccfaa289516e43c556f301e [file] [log] [blame]
Scott Bauer18a5bf22017-12-18 10:28:08 -07001/*
2 * Copyright (C) 2017 Intel Corporation.
3 *
4 * This file is released under the GPL.
5 */
6
7#include "dm.h"
8
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/blkdev.h>
12#include <linux/bio.h>
13#include <linux/slab.h>
14#include <linux/bitops.h>
15#include <linux/device-mapper.h>
16
17struct unstripe_c {
18 struct dm_dev *dev;
19 sector_t physical_start;
20
21 uint32_t stripes;
22
23 uint32_t unstripe;
24 sector_t unstripe_width;
25 sector_t unstripe_offset;
26
27 uint32_t chunk_size;
28 u8 chunk_shift;
29};
30
31#define DM_MSG_PREFIX "unstriped"
32
33static void cleanup_unstripe(struct unstripe_c *uc, struct dm_target *ti)
34{
35 if (uc->dev)
36 dm_put_device(ti, uc->dev);
37 kfree(uc);
38}
39
40/*
41 * Contruct an unstriped mapping.
42 * <number of stripes> <chunk size> <stripe #> <dev_path> <offset>
43 */
44static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
45{
46 struct unstripe_c *uc;
Scott Bauercc656612018-01-23 10:55:18 -070047 sector_t tmp_len;
Scott Bauer18a5bf22017-12-18 10:28:08 -070048 unsigned long long start;
49 char dummy;
50
51 if (argc != 5) {
52 ti->error = "Invalid number of arguments";
53 return -EINVAL;
54 }
55
56 uc = kzalloc(sizeof(*uc), GFP_KERNEL);
57 if (!uc) {
58 ti->error = "Memory allocation for unstriped context failed";
59 return -ENOMEM;
60 }
61
62 if (kstrtouint(argv[0], 10, &uc->stripes) || !uc->stripes) {
63 ti->error = "Invalid stripe count";
64 goto err;
65 }
66
67 if (kstrtouint(argv[1], 10, &uc->chunk_size) || !uc->chunk_size) {
68 ti->error = "Invalid chunk_size";
69 goto err;
70 }
71
Scott Bauer18a5bf22017-12-18 10:28:08 -070072 if (kstrtouint(argv[2], 10, &uc->unstripe)) {
73 ti->error = "Invalid stripe number";
74 goto err;
75 }
76
77 if (uc->unstripe > uc->stripes && uc->stripes > 1) {
78 ti->error = "Please provide stripe between [0, # of stripes]";
79 goto err;
80 }
81
82 if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &uc->dev)) {
83 ti->error = "Couldn't get striped device";
84 goto err;
85 }
86
87 if (sscanf(argv[4], "%llu%c", &start, &dummy) != 1) {
88 ti->error = "Invalid striped device offset";
89 goto err;
90 }
91 uc->physical_start = start;
92
93 uc->unstripe_offset = uc->unstripe * uc->chunk_size;
94 uc->unstripe_width = (uc->stripes - 1) * uc->chunk_size;
Heinz Mauelshagen2ae600c2018-02-01 19:06:09 +010095 uc->chunk_shift = is_power_of_2(uc->chunk_size) ? fls(uc->chunk_size) - 1 : 0;
Scott Bauer18a5bf22017-12-18 10:28:08 -070096
Scott Bauercc656612018-01-23 10:55:18 -070097 tmp_len = ti->len;
Scott Bauer18a5bf22017-12-18 10:28:08 -070098 if (sector_div(tmp_len, uc->chunk_size)) {
99 ti->error = "Target length not divisible by chunk size";
100 goto err;
101 }
102
103 if (dm_set_target_max_io_len(ti, uc->chunk_size)) {
104 ti->error = "Failed to set max io len";
105 goto err;
106 }
107
108 ti->private = uc;
109 return 0;
110err:
111 cleanup_unstripe(uc, ti);
112 return -EINVAL;
113}
114
115static void unstripe_dtr(struct dm_target *ti)
116{
117 struct unstripe_c *uc = ti->private;
118
119 cleanup_unstripe(uc, ti);
120}
121
122static sector_t map_to_core(struct dm_target *ti, struct bio *bio)
123{
124 struct unstripe_c *uc = ti->private;
125 sector_t sector = bio->bi_iter.bi_sector;
Heinz Mauelshagen2ae600c2018-02-01 19:06:09 +0100126 sector_t tmp_sector = sector;
Scott Bauer18a5bf22017-12-18 10:28:08 -0700127
128 /* Shift us up to the right "row" on the stripe */
Heinz Mauelshagen2ae600c2018-02-01 19:06:09 +0100129 if (uc->chunk_shift)
130 tmp_sector >>= uc->chunk_shift;
131 else
132 sector_div(tmp_sector, uc->chunk_size);
133
134 sector += uc->unstripe_width * tmp_sector;
Scott Bauer18a5bf22017-12-18 10:28:08 -0700135
136 /* Account for what stripe we're operating on */
Heinz Mauelshagen2ae600c2018-02-01 19:06:09 +0100137 return sector + uc->unstripe_offset;
Scott Bauer18a5bf22017-12-18 10:28:08 -0700138}
139
140static int unstripe_map(struct dm_target *ti, struct bio *bio)
141{
142 struct unstripe_c *uc = ti->private;
143
144 bio_set_dev(bio, uc->dev->bdev);
145 bio->bi_iter.bi_sector = map_to_core(ti, bio) + uc->physical_start;
146
147 return DM_MAPIO_REMAPPED;
148}
149
150static void unstripe_status(struct dm_target *ti, status_type_t type,
151 unsigned int status_flags, char *result, unsigned int maxlen)
152{
153 struct unstripe_c *uc = ti->private;
154 unsigned int sz = 0;
155
156 switch (type) {
157 case STATUSTYPE_INFO:
158 break;
159
160 case STATUSTYPE_TABLE:
161 DMEMIT("%d %llu %d %s %llu",
162 uc->stripes, (unsigned long long)uc->chunk_size, uc->unstripe,
163 uc->dev->name, (unsigned long long)uc->physical_start);
164 break;
165 }
166}
167
168static int unstripe_iterate_devices(struct dm_target *ti,
169 iterate_devices_callout_fn fn, void *data)
170{
171 struct unstripe_c *uc = ti->private;
172
173 return fn(ti, uc->dev, uc->physical_start, ti->len, data);
174}
175
176static void unstripe_io_hints(struct dm_target *ti,
177 struct queue_limits *limits)
178{
179 struct unstripe_c *uc = ti->private;
180
181 limits->chunk_sectors = uc->chunk_size;
182}
183
184static struct target_type unstripe_target = {
185 .name = "unstriped",
Heinz Mauelshagen2ae600c2018-02-01 19:06:09 +0100186 .version = {1, 1, 0},
Scott Bauer18a5bf22017-12-18 10:28:08 -0700187 .module = THIS_MODULE,
188 .ctr = unstripe_ctr,
189 .dtr = unstripe_dtr,
190 .map = unstripe_map,
191 .status = unstripe_status,
192 .iterate_devices = unstripe_iterate_devices,
193 .io_hints = unstripe_io_hints,
194};
195
196static int __init dm_unstripe_init(void)
197{
198 int r;
199
200 r = dm_register_target(&unstripe_target);
201 if (r < 0)
202 DMERR("target registration failed");
203
204 return r;
205}
206
207static void __exit dm_unstripe_exit(void)
208{
209 dm_unregister_target(&unstripe_target);
210}
211
212module_init(dm_unstripe_init);
213module_exit(dm_unstripe_exit);
214
215MODULE_DESCRIPTION(DM_NAME " unstriped target");
216MODULE_AUTHOR("Scott Bauer <scott.bauer@intel.com>");
217MODULE_LICENSE("GPL");