Blame view

drivers/md/dm-stripe.c 9.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*
   * Copyright (C) 2001-2003 Sistina Software (UK) Limited.
   *
   * This file is released under the GPL.
   */
586e80e6e   Mikulas Patocka   dm: remove dm hea...
6
  #include <linux/device-mapper.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
11
12
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/blkdev.h>
  #include <linux/bio.h>
  #include <linux/slab.h>
6f3c3f0af   vignesh babu   dm: use is_power_...
13
  #include <linux/log2.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14

72d948616   Alasdair G Kergon   [PATCH] dm: impro...
15
  #define DM_MSG_PREFIX "striped"
a25eb9446   Brian Wood   dm: stripe trigge...
16
  #define DM_IO_ERROR_THRESHOLD 15
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
17

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
  struct stripe {
  	struct dm_dev *dev;
  	sector_t physical_start;
a25eb9446   Brian Wood   dm: stripe trigge...
21
22
  
  	atomic_t error_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
  };
  
  struct stripe_c {
  	uint32_t stripes;
c96053b76   Mikulas Patocka   dm stripe: optimi...
27
28
  	int stripes_shift;
  	sector_t stripes_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
  
  	/* The size of this target / num. stripes */
  	sector_t stripe_width;
  
  	/* stripe chunk size */
  	uint32_t chunk_shift;
  	sector_t chunk_mask;
a25eb9446   Brian Wood   dm: stripe trigge...
36
37
38
39
40
  	/* Needed for handling events */
  	struct dm_target *ti;
  
  	/* Work struct used for triggering events*/
  	struct work_struct kstriped_ws;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
  	struct stripe stripe[0];
  };
a25eb9446   Brian Wood   dm: stripe trigge...
43
44
45
46
47
48
49
50
51
52
53
54
55
  static struct workqueue_struct *kstriped;
  
  /*
   * An event is triggered whenever a drive
   * drops out of a stripe volume.
   */
  static void trigger_event(struct work_struct *work)
  {
  	struct stripe_c *sc = container_of(work, struct stripe_c, kstriped_ws);
  
  	dm_table_event(sc->ti->table);
  
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
  static inline struct stripe_c *alloc_context(unsigned int stripes)
  {
  	size_t len;
d63a5ce3c   Mikulas Patocka   dm: publish array...
59
60
  	if (dm_array_too_big(sizeof(struct stripe_c), sizeof(struct stripe),
  			     stripes))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
69
70
71
72
73
  		return NULL;
  
  	len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes);
  
  	return kmalloc(len, GFP_KERNEL);
  }
  
  /*
   * Parse a single <dev> <sector> pair
   */
  static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
  		      unsigned int stripe, char **argv)
  {
4ee218cd6   Andrew Morton   [PATCH] dm: remov...
74
  	unsigned long long start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75

4ee218cd6   Andrew Morton   [PATCH] dm: remov...
76
  	if (sscanf(argv[1], "%llu", &start) != 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
  		return -EINVAL;
8215d6ec5   Nikanth Karthikesan   dm table: remove ...
78
  	if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
81
82
  			  &sc->stripe[stripe].dev))
  		return -ENXIO;
  
  	sc->stripe[stripe].physical_start = start;
a25eb9446   Brian Wood   dm: stripe trigge...
83

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  	return 0;
  }
  
  /*
   * Construct a striped mapping.
   * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
   */
  static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  {
  	struct stripe_c *sc;
  	sector_t width;
  	uint32_t stripes;
  	uint32_t chunk_size;
  	char *end;
  	int r;
  	unsigned int i;
  
  	if (argc < 2) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
102
  		ti->error = "Not enough arguments";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
  		return -EINVAL;
  	}
  
  	stripes = simple_strtoul(argv[0], &end, 10);
781248c1b   Nikanth Karthikesan   dm stripe: avoid ...
107
  	if (!stripes || *end) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
108
  		ti->error = "Invalid stripe count";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
  		return -EINVAL;
  	}
  
  	chunk_size = simple_strtoul(argv[1], &end, 10);
  	if (*end) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
114
  		ti->error = "Invalid chunk_size";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
  		return -EINVAL;
  	}
  
  	/*
  	 * chunk_size is a power of two
  	 */
6f3c3f0af   vignesh babu   dm: use is_power_...
121
  	if (!is_power_of_2(chunk_size) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  	    (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
123
  		ti->error = "Invalid chunk size";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  		return -EINVAL;
  	}
a22c96c73   Kevin Corry   [PATCH] dm: remov...
126
  	if (ti->len & (chunk_size - 1)) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
127
  		ti->error = "Target length not divisible by "
8ba32fde2   Kevin Corry   [PATCH] dm stripe...
128
129
130
  		    "chunk size";
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
  	width = ti->len;
  	if (sector_div(width, stripes)) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
133
  		ti->error = "Target length not divisible by "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
140
141
  		    "number of stripes";
  		return -EINVAL;
  	}
  
  	/*
  	 * Do we have enough arguments for that many stripes ?
  	 */
  	if (argc != (2 + 2 * stripes)) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
142
  		ti->error = "Not enough destinations "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
  			"specified";
  		return -EINVAL;
  	}
  
  	sc = alloc_context(stripes);
  	if (!sc) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
149
  		ti->error = "Memory allocation for striped context "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
  		    "failed";
  		return -ENOMEM;
  	}
a25eb9446   Brian Wood   dm: stripe trigge...
153
154
155
156
  	INIT_WORK(&sc->kstriped_ws, trigger_event);
  
  	/* Set pointer to dm target; used in trigger_event */
  	sc->ti = ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
  	sc->stripes = stripes;
  	sc->stripe_width = width;
c96053b76   Mikulas Patocka   dm stripe: optimi...
159
160
161
162
163
164
165
  
  	if (stripes & (stripes - 1))
  		sc->stripes_shift = -1;
  	else {
  		sc->stripes_shift = ffs(stripes) - 1;
  		sc->stripes_mask = ((sector_t) stripes) - 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  	ti->split_io = chunk_size;
374bf7e7f   Mikulas Patocka   dm: stripe suppor...
167
  	ti->num_flush_requests = stripes;
7b76ec11f   Mikulas Patocka   dm stripe: suppor...
168
  	ti->num_discard_requests = stripes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

c96053b76   Mikulas Patocka   dm stripe: optimi...
170
  	sc->chunk_shift = ffs(chunk_size) - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	sc->chunk_mask = ((sector_t) chunk_size) - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
179
180
  
  	/*
  	 * Get the stripe destinations.
  	 */
  	for (i = 0; i < stripes; i++) {
  		argv += 2;
  
  		r = get_stripe(ti, sc, i, argv);
  		if (r < 0) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
181
  			ti->error = "Couldn't parse stripe destination";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
  			while (i--)
  				dm_put_device(ti, sc->stripe[i].dev);
  			kfree(sc);
  			return r;
  		}
a25eb9446   Brian Wood   dm: stripe trigge...
187
  		atomic_set(&(sc->stripe[i].error_count), 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
  	}
  
  	ti->private = sc;
a25eb9446   Brian Wood   dm: stripe trigge...
191

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
196
197
198
199
200
201
  	return 0;
  }
  
  static void stripe_dtr(struct dm_target *ti)
  {
  	unsigned int i;
  	struct stripe_c *sc = (struct stripe_c *) ti->private;
  
  	for (i = 0; i < sc->stripes; i++)
  		dm_put_device(ti, sc->stripe[i].dev);
a25eb9446   Brian Wood   dm: stripe trigge...
202
  	flush_workqueue(kstriped);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
  	kfree(sc);
  }
65988525a   Mikulas Patocka   dm stripe: move s...
205
206
207
208
209
  static void stripe_map_sector(struct stripe_c *sc, sector_t sector,
  			      uint32_t *stripe, sector_t *result)
  {
  	sector_t offset = dm_target_offset(sc->ti, sector);
  	sector_t chunk = offset >> sc->chunk_shift;
c96053b76   Mikulas Patocka   dm stripe: optimi...
210
211
212
213
214
215
  	if (sc->stripes_shift < 0)
  		*stripe = sector_div(chunk, sc->stripes);
  	else {
  		*stripe = chunk & sc->stripes_mask;
  		chunk >>= sc->stripes_shift;
  	}
65988525a   Mikulas Patocka   dm stripe: move s...
216
217
  	*result = (chunk << sc->chunk_shift) | (offset & sc->chunk_mask);
  }
7b76ec11f   Mikulas Patocka   dm stripe: suppor...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
  				    uint32_t target_stripe, sector_t *result)
  {
  	uint32_t stripe;
  
  	stripe_map_sector(sc, sector, &stripe, result);
  	if (stripe == target_stripe)
  		return;
  	*result &= ~sc->chunk_mask;			/* round down */
  	if (target_stripe < stripe)
  		*result += sc->chunk_mask + 1;		/* next chunk */
  }
  
  static int stripe_map_discard(struct stripe_c *sc, struct bio *bio,
  			      uint32_t target_stripe)
  {
  	sector_t begin, end;
  
  	stripe_map_range_sector(sc, bio->bi_sector, target_stripe, &begin);
  	stripe_map_range_sector(sc, bio->bi_sector + bio_sectors(bio),
  				target_stripe, &end);
  	if (begin < end) {
  		bio->bi_bdev = sc->stripe[target_stripe].dev->bdev;
  		bio->bi_sector = begin + sc->stripe[target_stripe].physical_start;
  		bio->bi_size = to_bytes(end - begin);
  		return DM_MAPIO_REMAPPED;
  	} else {
  		/* The range doesn't map to the target stripe */
  		bio_endio(bio, 0);
  		return DM_MAPIO_SUBMITTED;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
  static int stripe_map(struct dm_target *ti, struct bio *bio,
  		      union map_info *map_context)
  {
65988525a   Mikulas Patocka   dm stripe: move s...
253
  	struct stripe_c *sc = ti->private;
374bf7e7f   Mikulas Patocka   dm: stripe suppor...
254
  	uint32_t stripe;
57cba5d36   Mike Snitzer   dm: rename map_in...
255
  	unsigned target_request_nr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256

374bf7e7f   Mikulas Patocka   dm: stripe suppor...
257
  	if (unlikely(bio_empty_barrier(bio))) {
57cba5d36   Mike Snitzer   dm: rename map_in...
258
259
260
  		target_request_nr = map_context->target_request_nr;
  		BUG_ON(target_request_nr >= sc->stripes);
  		bio->bi_bdev = sc->stripe[target_request_nr].dev->bdev;
374bf7e7f   Mikulas Patocka   dm: stripe suppor...
261
262
  		return DM_MAPIO_REMAPPED;
  	}
7b76ec11f   Mikulas Patocka   dm stripe: suppor...
263
264
265
266
267
  	if (unlikely(bio->bi_rw & REQ_DISCARD)) {
  		target_request_nr = map_context->target_request_nr;
  		BUG_ON(target_request_nr >= sc->stripes);
  		return stripe_map_discard(sc, bio, target_request_nr);
  	}
374bf7e7f   Mikulas Patocka   dm: stripe suppor...
268

65988525a   Mikulas Patocka   dm stripe: move s...
269
  	stripe_map_sector(sc, bio->bi_sector, &stripe, &bio->bi_sector);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270

65988525a   Mikulas Patocka   dm stripe: move s...
271
  	bio->bi_sector += sc->stripe[stripe].physical_start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  	bio->bi_bdev = sc->stripe[stripe].dev->bdev;
65988525a   Mikulas Patocka   dm stripe: move s...
273

d2a7ad29a   Kiyoshi Ueda   [PATCH] dm: map a...
274
  	return DM_MAPIO_REMAPPED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  }
4f7f5c675   Brian Wood   dm: stripe enhanc...
276
277
278
279
280
281
282
283
284
285
286
287
  /*
   * Stripe status:
   *
   * INFO
   * #stripes [stripe_name <stripe_name>] [group word count]
   * [error count 'A|D' <error count 'A|D'>]
   *
   * TABLE
   * #stripes [stripe chunk size]
   * [stripe_name physical_start <stripe_name physical_start>]
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
  static int stripe_status(struct dm_target *ti,
  			 status_type_t type, char *result, unsigned int maxlen)
  {
  	struct stripe_c *sc = (struct stripe_c *) ti->private;
4f7f5c675   Brian Wood   dm: stripe enhanc...
292
  	char buffer[sc->stripes + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
  	unsigned int sz = 0;
  	unsigned int i;
  
  	switch (type) {
  	case STATUSTYPE_INFO:
4f7f5c675   Brian Wood   dm: stripe enhanc...
298
299
300
301
302
303
304
305
  		DMEMIT("%d ", sc->stripes);
  		for (i = 0; i < sc->stripes; i++)  {
  			DMEMIT("%s ", sc->stripe[i].dev->name);
  			buffer[i] = atomic_read(&(sc->stripe[i].error_count)) ?
  				'D' : 'A';
  		}
  		buffer[i] = '\0';
  		DMEMIT("1 %s", buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
  		break;
  
  	case STATUSTYPE_TABLE:
4ee218cd6   Andrew Morton   [PATCH] dm: remov...
309
310
  		DMEMIT("%d %llu", sc->stripes,
  			(unsigned long long)sc->chunk_mask + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  		for (i = 0; i < sc->stripes; i++)
4ee218cd6   Andrew Morton   [PATCH] dm: remov...
312
313
  			DMEMIT(" %s %llu", sc->stripe[i].dev->name,
  			    (unsigned long long)sc->stripe[i].physical_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
  		break;
  	}
  	return 0;
  }
a25eb9446   Brian Wood   dm: stripe trigge...
318
319
320
321
322
323
324
325
326
  static int stripe_end_io(struct dm_target *ti, struct bio *bio,
  			 int error, union map_info *map_context)
  {
  	unsigned i;
  	char major_minor[16];
  	struct stripe_c *sc = ti->private;
  
  	if (!error)
  		return 0; /* I/O complete */
7b6d91dae   Christoph Hellwig   block: unify flag...
327
  	if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD))
a25eb9446   Brian Wood   dm: stripe trigge...
328
329
330
331
332
333
334
  		return error;
  
  	if (error == -EOPNOTSUPP)
  		return error;
  
  	memset(major_minor, 0, sizeof(major_minor));
  	sprintf(major_minor, "%d:%d",
f331c0296   Tejun Heo   block: don't depe...
335
336
  		MAJOR(disk_devt(bio->bi_bdev->bd_disk)),
  		MINOR(disk_devt(bio->bi_bdev->bd_disk)));
a25eb9446   Brian Wood   dm: stripe trigge...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  
  	/*
  	 * Test to see which stripe drive triggered the event
  	 * and increment error count for all stripes on that device.
  	 * If the error count for a given device exceeds the threshold
  	 * value we will no longer trigger any further events.
  	 */
  	for (i = 0; i < sc->stripes; i++)
  		if (!strcmp(sc->stripe[i].dev->name, major_minor)) {
  			atomic_inc(&(sc->stripe[i].error_count));
  			if (atomic_read(&(sc->stripe[i].error_count)) <
  			    DM_IO_ERROR_THRESHOLD)
  				queue_work(kstriped, &sc->kstriped_ws);
  		}
  
  	return error;
  }
af4874e03   Mike Snitzer   dm target:s intro...
354
355
356
357
358
359
  static int stripe_iterate_devices(struct dm_target *ti,
  				  iterate_devices_callout_fn fn, void *data)
  {
  	struct stripe_c *sc = ti->private;
  	int ret = 0;
  	unsigned i = 0;
5dea271b6   Mike Snitzer   dm table: pass co...
360
  	do {
af4874e03   Mike Snitzer   dm target:s intro...
361
  		ret = fn(ti, sc->stripe[i].dev,
5dea271b6   Mike Snitzer   dm table: pass co...
362
363
364
  			 sc->stripe[i].physical_start,
  			 sc->stripe_width, data);
  	} while (!ret && ++i < sc->stripes);
af4874e03   Mike Snitzer   dm target:s intro...
365
366
367
  
  	return ret;
  }
40bea4312   Mike Snitzer   dm stripe: expose...
368
369
370
371
372
373
374
  static void stripe_io_hints(struct dm_target *ti,
  			    struct queue_limits *limits)
  {
  	struct stripe_c *sc = ti->private;
  	unsigned chunk_size = (sc->chunk_mask + 1) << 9;
  
  	blk_limits_io_min(limits, chunk_size);
3c5820c74   Martin K. Petersen   block: Optimal I/...
375
  	blk_limits_io_opt(limits, chunk_size * sc->stripes);
40bea4312   Mike Snitzer   dm stripe: expose...
376
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
  static struct target_type stripe_target = {
  	.name   = "striped",
40bea4312   Mike Snitzer   dm stripe: expose...
379
  	.version = {1, 3, 0},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
  	.module = THIS_MODULE,
  	.ctr    = stripe_ctr,
  	.dtr    = stripe_dtr,
  	.map    = stripe_map,
a25eb9446   Brian Wood   dm: stripe trigge...
384
  	.end_io = stripe_end_io,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  	.status = stripe_status,
af4874e03   Mike Snitzer   dm target:s intro...
386
  	.iterate_devices = stripe_iterate_devices,
40bea4312   Mike Snitzer   dm stripe: expose...
387
  	.io_hints = stripe_io_hints,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
390
391
392
393
394
  };
  
  int __init dm_stripe_init(void)
  {
  	int r;
  
  	r = dm_register_target(&stripe_target);
6edebdee4   Heinz Mauelshagen   dm stripe: fix in...
395
  	if (r < 0) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
396
  		DMWARN("target registration failed");
6edebdee4   Heinz Mauelshagen   dm stripe: fix in...
397
398
  		return r;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399

a25eb9446   Brian Wood   dm: stripe trigge...
400
401
402
403
404
405
  	kstriped = create_singlethread_workqueue("kstriped");
  	if (!kstriped) {
  		DMERR("failed to create workqueue kstriped");
  		dm_unregister_target(&stripe_target);
  		return -ENOMEM;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
  	return r;
  }
  
  void dm_stripe_exit(void)
  {
10d3bd09a   Mikulas Patocka   dm: consolidate t...
411
  	dm_unregister_target(&stripe_target);
a25eb9446   Brian Wood   dm: stripe trigge...
412
  	destroy_workqueue(kstriped);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  	return;
  }