diff options
Diffstat (limited to 'drivers/md/dm-stripe.c')
-rw-r--r-- | drivers/md/dm-stripe.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c new file mode 100644 index 000000000000..ab89278a56bf --- /dev/null +++ b/drivers/md/dm-stripe.c | |||
@@ -0,0 +1,234 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001-2003 Sistina Software (UK) Limited. | ||
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 | |||
15 | struct stripe { | ||
16 | struct dm_dev *dev; | ||
17 | sector_t physical_start; | ||
18 | }; | ||
19 | |||
20 | struct stripe_c { | ||
21 | uint32_t stripes; | ||
22 | |||
23 | /* The size of this target / num. stripes */ | ||
24 | sector_t stripe_width; | ||
25 | |||
26 | /* stripe chunk size */ | ||
27 | uint32_t chunk_shift; | ||
28 | sector_t chunk_mask; | ||
29 | |||
30 | struct stripe stripe[0]; | ||
31 | }; | ||
32 | |||
33 | static inline struct stripe_c *alloc_context(unsigned int stripes) | ||
34 | { | ||
35 | size_t len; | ||
36 | |||
37 | if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe), | ||
38 | stripes)) | ||
39 | return NULL; | ||
40 | |||
41 | len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes); | ||
42 | |||
43 | return kmalloc(len, GFP_KERNEL); | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * Parse a single <dev> <sector> pair | ||
48 | */ | ||
49 | static int get_stripe(struct dm_target *ti, struct stripe_c *sc, | ||
50 | unsigned int stripe, char **argv) | ||
51 | { | ||
52 | sector_t start; | ||
53 | |||
54 | if (sscanf(argv[1], SECTOR_FORMAT, &start) != 1) | ||
55 | return -EINVAL; | ||
56 | |||
57 | if (dm_get_device(ti, argv[0], start, sc->stripe_width, | ||
58 | dm_table_get_mode(ti->table), | ||
59 | &sc->stripe[stripe].dev)) | ||
60 | return -ENXIO; | ||
61 | |||
62 | sc->stripe[stripe].physical_start = start; | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Construct a striped mapping. | ||
68 | * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+ | ||
69 | */ | ||
70 | static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) | ||
71 | { | ||
72 | struct stripe_c *sc; | ||
73 | sector_t width; | ||
74 | uint32_t stripes; | ||
75 | uint32_t chunk_size; | ||
76 | char *end; | ||
77 | int r; | ||
78 | unsigned int i; | ||
79 | |||
80 | if (argc < 2) { | ||
81 | ti->error = "dm-stripe: Not enough arguments"; | ||
82 | return -EINVAL; | ||
83 | } | ||
84 | |||
85 | stripes = simple_strtoul(argv[0], &end, 10); | ||
86 | if (*end) { | ||
87 | ti->error = "dm-stripe: Invalid stripe count"; | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | chunk_size = simple_strtoul(argv[1], &end, 10); | ||
92 | if (*end) { | ||
93 | ti->error = "dm-stripe: Invalid chunk_size"; | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * chunk_size is a power of two | ||
99 | */ | ||
100 | if (!chunk_size || (chunk_size & (chunk_size - 1)) || | ||
101 | (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) { | ||
102 | ti->error = "dm-stripe: Invalid chunk size"; | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | width = ti->len; | ||
107 | if (sector_div(width, stripes)) { | ||
108 | ti->error = "dm-stripe: Target length not divisable by " | ||
109 | "number of stripes"; | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Do we have enough arguments for that many stripes ? | ||
115 | */ | ||
116 | if (argc != (2 + 2 * stripes)) { | ||
117 | ti->error = "dm-stripe: Not enough destinations " | ||
118 | "specified"; | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | |||
122 | sc = alloc_context(stripes); | ||
123 | if (!sc) { | ||
124 | ti->error = "dm-stripe: Memory allocation for striped context " | ||
125 | "failed"; | ||
126 | return -ENOMEM; | ||
127 | } | ||
128 | |||
129 | sc->stripes = stripes; | ||
130 | sc->stripe_width = width; | ||
131 | ti->split_io = chunk_size; | ||
132 | |||
133 | sc->chunk_mask = ((sector_t) chunk_size) - 1; | ||
134 | for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++) | ||
135 | chunk_size >>= 1; | ||
136 | sc->chunk_shift--; | ||
137 | |||
138 | /* | ||
139 | * Get the stripe destinations. | ||
140 | */ | ||
141 | for (i = 0; i < stripes; i++) { | ||
142 | argv += 2; | ||
143 | |||
144 | r = get_stripe(ti, sc, i, argv); | ||
145 | if (r < 0) { | ||
146 | ti->error = "dm-stripe: Couldn't parse stripe " | ||
147 | "destination"; | ||
148 | while (i--) | ||
149 | dm_put_device(ti, sc->stripe[i].dev); | ||
150 | kfree(sc); | ||
151 | return r; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | ti->private = sc; | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void stripe_dtr(struct dm_target *ti) | ||
160 | { | ||
161 | unsigned int i; | ||
162 | struct stripe_c *sc = (struct stripe_c *) ti->private; | ||
163 | |||
164 | for (i = 0; i < sc->stripes; i++) | ||
165 | dm_put_device(ti, sc->stripe[i].dev); | ||
166 | |||
167 | kfree(sc); | ||
168 | } | ||
169 | |||
170 | static int stripe_map(struct dm_target *ti, struct bio *bio, | ||
171 | union map_info *map_context) | ||
172 | { | ||
173 | struct stripe_c *sc = (struct stripe_c *) ti->private; | ||
174 | |||
175 | sector_t offset = bio->bi_sector - ti->begin; | ||
176 | sector_t chunk = offset >> sc->chunk_shift; | ||
177 | uint32_t stripe = sector_div(chunk, sc->stripes); | ||
178 | |||
179 | bio->bi_bdev = sc->stripe[stripe].dev->bdev; | ||
180 | bio->bi_sector = sc->stripe[stripe].physical_start + | ||
181 | (chunk << sc->chunk_shift) + (offset & sc->chunk_mask); | ||
182 | return 1; | ||
183 | } | ||
184 | |||
185 | static int stripe_status(struct dm_target *ti, | ||
186 | status_type_t type, char *result, unsigned int maxlen) | ||
187 | { | ||
188 | struct stripe_c *sc = (struct stripe_c *) ti->private; | ||
189 | unsigned int sz = 0; | ||
190 | unsigned int i; | ||
191 | |||
192 | switch (type) { | ||
193 | case STATUSTYPE_INFO: | ||
194 | result[0] = '\0'; | ||
195 | break; | ||
196 | |||
197 | case STATUSTYPE_TABLE: | ||
198 | DMEMIT("%d " SECTOR_FORMAT, sc->stripes, sc->chunk_mask + 1); | ||
199 | for (i = 0; i < sc->stripes; i++) | ||
200 | DMEMIT(" %s " SECTOR_FORMAT, sc->stripe[i].dev->name, | ||
201 | sc->stripe[i].physical_start); | ||
202 | break; | ||
203 | } | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static struct target_type stripe_target = { | ||
208 | .name = "striped", | ||
209 | .version= {1, 0, 2}, | ||
210 | .module = THIS_MODULE, | ||
211 | .ctr = stripe_ctr, | ||
212 | .dtr = stripe_dtr, | ||
213 | .map = stripe_map, | ||
214 | .status = stripe_status, | ||
215 | }; | ||
216 | |||
217 | int __init dm_stripe_init(void) | ||
218 | { | ||
219 | int r; | ||
220 | |||
221 | r = dm_register_target(&stripe_target); | ||
222 | if (r < 0) | ||
223 | DMWARN("striped target registration failed"); | ||
224 | |||
225 | return r; | ||
226 | } | ||
227 | |||
228 | void dm_stripe_exit(void) | ||
229 | { | ||
230 | if (dm_unregister_target(&stripe_target)) | ||
231 | DMWARN("striped target unregistration failed"); | ||
232 | |||
233 | return; | ||
234 | } | ||