Blame view

drivers/md/multipath.c 14.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * multipath.c : Multiple Devices driver for Linux
   *
   * Copyright (C) 1999, 2000, 2001 Ingo Molnar, Red Hat
   *
   * Copyright (C) 1996, 1997, 1998 Ingo Molnar, Miguel de Icaza, Gadi Oxman
   *
   * MULTIPATH management functions.
   *
   * derived from raid1.c.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2, or (at your option)
   * any later version.
   *
   * You should have received a copy of the GNU General Public License
   * (for example /usr/src/linux/COPYING); if not, write to the Free
   * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
bff61975b   NeilBrown   md: move lots of ...
21
  #include <linux/blkdev.h>
056075c76   Paul Gortmaker   md: Add module.h ...
22
  #include <linux/module.h>
bff61975b   NeilBrown   md: move lots of ...
23
  #include <linux/raid/md_u.h>
bff61975b   NeilBrown   md: move lots of ...
24
  #include <linux/seq_file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
43b2e5d86   NeilBrown   md: move md_k.h f...
26
  #include "md.h"
ef740c372   Christoph Hellwig   md: move headers ...
27
  #include "multipath.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  
  #define MAX_WORK_PER_DISK 128
  
  #define	NR_RESERVED_BUFS	32
69724e28c   NeilBrown   md/multipath: typ...
32
  static int multipath_map (struct mpconf *conf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
  {
  	int i, disks = conf->raid_disks;
  
  	/*
  	 * Later we do read balancing on the read side 
  	 * now we use the first available disk.
  	 */
  
  	rcu_read_lock();
  	for (i = 0; i < disks; i++) {
3cb030020   NeilBrown   md: removing type...
43
  		struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
b2d444d7a   NeilBrown   [PATCH] md: conve...
44
  		if (rdev && test_bit(In_sync, &rdev->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  			atomic_inc(&rdev->nr_pending);
  			rcu_read_unlock();
  			return i;
  		}
  	}
  	rcu_read_unlock();
  
  	printk(KERN_ERR "multipath_map(): no more operational IO paths?
  ");
  	return (-1);
  }
  
  static void multipath_reschedule_retry (struct multipath_bh *mp_bh)
  {
  	unsigned long flags;
fd01b88c7   NeilBrown   md: remove typede...
60
  	struct mddev *mddev = mp_bh->mddev;
69724e28c   NeilBrown   md/multipath: typ...
61
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  
  	spin_lock_irqsave(&conf->device_lock, flags);
  	list_add(&mp_bh->retry_list, &conf->retry_list);
  	spin_unlock_irqrestore(&conf->device_lock, flags);
  	md_wakeup_thread(mddev->thread);
  }
  
  
  /*
   * multipath_end_bh_io() is called when we have finished servicing a multipathed
   * operation and are ready to return a success/failure code to the buffer
   * cache layer.
   */
  static void multipath_end_bh_io (struct multipath_bh *mp_bh, int err)
  {
  	struct bio *bio = mp_bh->master_bio;
69724e28c   NeilBrown   md/multipath: typ...
78
  	struct mpconf *conf = mp_bh->mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

6712ecf8f   NeilBrown   Drop 'size' argum...
80
  	bio_endio(bio, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
  	mempool_free(mp_bh, conf->pool);
  }
6712ecf8f   NeilBrown   Drop 'size' argum...
83
  static void multipath_end_request(struct bio *bio, int error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
  {
  	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
7b92813c3   H Hartley Sweeten   drivers/md: Remov...
86
  	struct multipath_bh *mp_bh = bio->bi_private;
69724e28c   NeilBrown   md/multipath: typ...
87
  	struct mpconf *conf = mp_bh->mddev->private;
3cb030020   NeilBrown   md: removing type...
88
  	struct md_rdev *rdev = conf->multipaths[mp_bh->path].rdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  	if (uptodate)
  		multipath_end_bh_io(mp_bh, 0);
7b6d91dae   Christoph Hellwig   block: unify flag...
92
  	else if (!(bio->bi_rw & REQ_RAHEAD)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
102
103
104
105
  		/*
  		 * oops, IO error:
  		 */
  		char b[BDEVNAME_SIZE];
  		md_error (mp_bh->mddev, rdev);
  		printk(KERN_ERR "multipath: %s: rescheduling sector %llu
  ", 
  		       bdevname(rdev->bdev,b), 
  		       (unsigned long long)bio->bi_sector);
  		multipath_reschedule_retry(mp_bh);
  	} else
  		multipath_end_bh_io(mp_bh, error);
  	rdev_dec_pending(rdev, conf->mddev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  }
b4fdcb02f   Linus Torvalds   Merge branch 'for...
107
  static void multipath_make_request(struct mddev *mddev, struct bio * bio)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  {
69724e28c   NeilBrown   md/multipath: typ...
109
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
  	struct multipath_bh * mp_bh;
  	struct multipath_info *multipath;
e9c7469bb   Tejun Heo   md: implment REQ_...
112
113
  	if (unlikely(bio->bi_rw & REQ_FLUSH)) {
  		md_flush_request(mddev, bio);
5a7bbad27   Christoph Hellwig   block: remove sup...
114
  		return;
e5dcdd80a   NeilBrown   [PATCH] md: fail ...
115
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
  	mp_bh = mempool_alloc(conf->pool, GFP_NOIO);
  
  	mp_bh->master_bio = bio;
  	mp_bh->mddev = mddev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  	mp_bh->path = multipath_map(conf);
  	if (mp_bh->path < 0) {
6712ecf8f   NeilBrown   Drop 'size' argum...
122
  		bio_endio(bio, -EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  		mempool_free(mp_bh, conf->pool);
5a7bbad27   Christoph Hellwig   block: remove sup...
124
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
  	}
  	multipath = conf->multipaths + mp_bh->path;
  
  	mp_bh->bio = *bio;
  	mp_bh->bio.bi_sector += multipath->rdev->data_offset;
  	mp_bh->bio.bi_bdev = multipath->rdev->bdev;
7b6d91dae   Christoph Hellwig   block: unify flag...
131
  	mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  	mp_bh->bio.bi_end_io = multipath_end_request;
  	mp_bh->bio.bi_private = mp_bh;
  	generic_make_request(&mp_bh->bio);
5a7bbad27   Christoph Hellwig   block: remove sup...
135
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  }
fd01b88c7   NeilBrown   md: remove typede...
137
  static void multipath_status (struct seq_file *seq, struct mddev *mddev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
69724e28c   NeilBrown   md/multipath: typ...
139
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
  	int i;
  	
  	seq_printf (seq, " [%d/%d] [", conf->raid_disks,
92f861a72   NeilBrown   md/multipath: dis...
143
  		    conf->raid_disks - mddev->degraded);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
  	for (i = 0; i < conf->raid_disks; i++)
  		seq_printf (seq, "%s",
  			       conf->multipaths[i].rdev && 
b2d444d7a   NeilBrown   [PATCH] md: conve...
147
  			       test_bit(In_sync, &conf->multipaths[i].rdev->flags) ? "U" : "_");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
  	seq_printf (seq, "]");
  }
0d1292282   NeilBrown   [PATCH] md: defin...
150
151
  static int multipath_congested(void *data, int bits)
  {
fd01b88c7   NeilBrown   md: remove typede...
152
  	struct mddev *mddev = data;
69724e28c   NeilBrown   md/multipath: typ...
153
  	struct mpconf *conf = mddev->private;
0d1292282   NeilBrown   [PATCH] md: defin...
154
  	int i, ret = 0;
3fa841d7e   NeilBrown   md: report device...
155
156
  	if (mddev_congested(mddev, bits))
  		return 1;
0d1292282   NeilBrown   [PATCH] md: defin...
157
158
  	rcu_read_lock();
  	for (i = 0; i < mddev->raid_disks ; i++) {
3cb030020   NeilBrown   md: removing type...
159
  		struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
0d1292282   NeilBrown   [PATCH] md: defin...
160
  		if (rdev && !test_bit(Faulty, &rdev->flags)) {
165125e1e   Jens Axboe   [BLOCK] Get rid o...
161
  			struct request_queue *q = bdev_get_queue(rdev->bdev);
0d1292282   NeilBrown   [PATCH] md: defin...
162
163
164
165
166
167
168
169
170
171
172
  
  			ret |= bdi_congested(&q->backing_dev_info, bits);
  			/* Just like multipath_map, we just check the
  			 * first available device
  			 */
  			break;
  		}
  	}
  	rcu_read_unlock();
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
176
  
  /*
   * Careful, this can execute in IRQ contexts as well!
   */
fd01b88c7   NeilBrown   md: remove typede...
177
  static void multipath_error (struct mddev *mddev, struct md_rdev *rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  {
69724e28c   NeilBrown   md/multipath: typ...
179
  	struct mpconf *conf = mddev->private;
6f8d0c77c   NeilBrown   md: make error_ha...
180
  	char b[BDEVNAME_SIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181

92f861a72   NeilBrown   md/multipath: dis...
182
  	if (conf->raid_disks - mddev->degraded <= 1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
  		/*
  		 * Uh oh, we can do nothing if this is our last path, but
  		 * first check if this is a queued request for a device
  		 * which has just failed.
  		 */
  		printk(KERN_ALERT 
6f8d0c77c   NeilBrown   md: make error_ha...
189
190
  		       "multipath: only one IO path left and IO error.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  		/* leave it active... it's all we have */
6f8d0c77c   NeilBrown   md: make error_ha...
192
193
194
195
196
197
198
199
200
201
  		return;
  	}
  	/*
  	 * Mark disk as unusable
  	 */
  	if (test_and_clear_bit(In_sync, &rdev->flags)) {
  		unsigned long flags;
  		spin_lock_irqsave(&conf->device_lock, flags);
  		mddev->degraded++;
  		spin_unlock_irqrestore(&conf->device_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  	}
6f8d0c77c   NeilBrown   md: make error_ha...
203
204
205
206
207
208
209
210
211
212
  	set_bit(Faulty, &rdev->flags);
  	set_bit(MD_CHANGE_DEVS, &mddev->flags);
  	printk(KERN_ALERT "multipath: IO failure on %s,"
  	       " disabling IO path.
  "
  	       "multipath: Operation continuing"
  	       " on %d IO paths.
  ",
  	       bdevname(rdev->bdev, b),
  	       conf->raid_disks - mddev->degraded);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  }
69724e28c   NeilBrown   md/multipath: typ...
214
  static void print_multipath_conf (struct mpconf *conf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
220
221
222
223
224
225
  {
  	int i;
  	struct multipath_info *tmp;
  
  	printk("MULTIPATH conf printout:
  ");
  	if (!conf) {
  		printk("(conf==NULL)
  ");
  		return;
  	}
92f861a72   NeilBrown   md/multipath: dis...
226
227
  	printk(" --- wd:%d rd:%d
  ", conf->raid_disks - conf->mddev->degraded,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
232
233
234
235
  			 conf->raid_disks);
  
  	for (i = 0; i < conf->raid_disks; i++) {
  		char b[BDEVNAME_SIZE];
  		tmp = conf->multipaths + i;
  		if (tmp->rdev)
  			printk(" disk%d, o:%d, dev:%s
  ",
b2d444d7a   NeilBrown   [PATCH] md: conve...
236
  				i,!test_bit(Faulty, &tmp->rdev->flags),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  			       bdevname(tmp->rdev->bdev,b));
  	}
  }
fd01b88c7   NeilBrown   md: remove typede...
240
  static int multipath_add_disk(struct mddev *mddev, struct md_rdev *rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  {
69724e28c   NeilBrown   md/multipath: typ...
242
  	struct mpconf *conf = mddev->private;
352d768b1   Jesper Juhl   [PATCH] Decrease ...
243
  	struct request_queue *q;
199050ea1   Neil Brown   rationalise retur...
244
  	int err = -EEXIST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
  	int path;
  	struct multipath_info *p;
6c2fce2ef   Neil Brown   Support adding a ...
247
248
249
250
251
  	int first = 0;
  	int last = mddev->raid_disks - 1;
  
  	if (rdev->raid_disk >= 0)
  		first = last = rdev->raid_disk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  
  	print_multipath_conf(conf);
6c2fce2ef   Neil Brown   Support adding a ...
254
  	for (path = first; path <= last; path++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  		if ((p=conf->multipaths+path)->rdev == NULL) {
352d768b1   Jesper Juhl   [PATCH] Decrease ...
256
  			q = rdev->bdev->bd_disk->queue;
8f6c2e4b3   Martin K. Petersen   md: Use new topol...
257
258
  			disk_stack_limits(mddev->gendisk, rdev->bdev,
  					  rdev->data_offset << 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  
  		/* as we don't honour merge_bvec_fn, we must never risk
627a2d3c2   NeilBrown   md: deal with mer...
261
262
  		 * violating it, so limit ->max_segments to one, lying
  		 * within a single page.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
  		 * (Note: it is very unlikely that a device with
  		 * merge_bvec_fn will be involved in multipath.)
  		 */
627a2d3c2   NeilBrown   md: deal with mer...
266
267
268
269
270
  			if (q->merge_bvec_fn) {
  				blk_queue_max_segments(mddev->queue, 1);
  				blk_queue_segment_boundary(mddev->queue,
  							   PAGE_CACHE_SIZE - 1);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

6f8d0c77c   NeilBrown   md: make error_ha...
272
  			spin_lock_irq(&conf->device_lock);
750a8f3e8   NeilBrown   [PATCH] md: fix u...
273
  			mddev->degraded--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  			rdev->raid_disk = path;
b2d444d7a   NeilBrown   [PATCH] md: conve...
275
  			set_bit(In_sync, &rdev->flags);
6f8d0c77c   NeilBrown   md: make error_ha...
276
  			spin_unlock_irq(&conf->device_lock);
d6065f7bf   Suzanne Wood   [PATCH] md: provi...
277
  			rcu_assign_pointer(p->rdev, rdev);
199050ea1   Neil Brown   rationalise retur...
278
  			err = 0;
ac5e7113e   Andre Noll   md: Push down dat...
279
  			md_integrity_add_rdev(rdev, mddev);
199050ea1   Neil Brown   rationalise retur...
280
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
  		}
  
  	print_multipath_conf(conf);
199050ea1   Neil Brown   rationalise retur...
284
285
  
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  }
b8321b68d   NeilBrown   md: change hot_re...
287
  static int multipath_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  {
69724e28c   NeilBrown   md/multipath: typ...
289
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  	int err = 0;
b8321b68d   NeilBrown   md: change hot_re...
291
  	int number = rdev->raid_disk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
  	struct multipath_info *p = conf->multipaths + number;
  
  	print_multipath_conf(conf);
b8321b68d   NeilBrown   md: change hot_re...
295
  	if (rdev == p->rdev) {
b2d444d7a   NeilBrown   [PATCH] md: conve...
296
  		if (test_bit(In_sync, &rdev->flags) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  		    atomic_read(&rdev->nr_pending)) {
dfc706450   NeilBrown   md: restart recov...
298
299
300
  			printk(KERN_ERR "hot-remove-disk, slot %d is identified"
  			       " but is still operational!
  ", number);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
304
  			err = -EBUSY;
  			goto abort;
  		}
  		p->rdev = NULL;
fbd568a3e   Paul E. McKenney   [PATCH] Change sy...
305
  		synchronize_rcu();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
  		if (atomic_read(&rdev->nr_pending)) {
  			/* lost the race, try later */
  			err = -EBUSY;
  			p->rdev = rdev;
ac5e7113e   Andre Noll   md: Push down dat...
310
  			goto abort;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  		}
a91a2785b   Martin K. Petersen   block: Require su...
312
  		err = md_integrity_register(mddev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  	}
  abort:
  
  	print_multipath_conf(conf);
  	return err;
  }
  
  
  
  /*
   * This is a kernel thread which:
   *
   *	1.	Retries failed read operations on working multipaths.
   *	2.	Updates the raid superblock when problems encounter.
   *	3.	Performs writes following reads for array syncronising.
   */
fd01b88c7   NeilBrown   md: remove typede...
329
  static void multipathd (struct mddev *mddev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
  {
  	struct multipath_bh *mp_bh;
  	struct bio *bio;
  	unsigned long flags;
69724e28c   NeilBrown   md/multipath: typ...
334
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
  	struct list_head *head = &conf->retry_list;
  
  	md_check_recovery(mddev);
  	for (;;) {
  		char b[BDEVNAME_SIZE];
  		spin_lock_irqsave(&conf->device_lock, flags);
  		if (list_empty(head))
  			break;
  		mp_bh = list_entry(head->prev, struct multipath_bh, retry_list);
  		list_del(head->prev);
  		spin_unlock_irqrestore(&conf->device_lock, flags);
  
  		bio = &mp_bh->bio;
  		bio->bi_sector = mp_bh->master_bio->bi_sector;
  		
  		if ((mp_bh->path = multipath_map (conf))<0) {
  			printk(KERN_ALERT "multipath: %s: unrecoverable IO read"
  				" error for block %llu
  ",
  				bdevname(bio->bi_bdev,b),
  				(unsigned long long)bio->bi_sector);
  			multipath_end_bh_io(mp_bh, -EIO);
  		} else {
  			printk(KERN_ERR "multipath: %s: redirecting sector %llu"
  				" to another IO path
  ",
  				bdevname(bio->bi_bdev,b),
  				(unsigned long long)bio->bi_sector);
  			*bio = *(mp_bh->master_bio);
  			bio->bi_sector += conf->multipaths[mp_bh->path].rdev->data_offset;
  			bio->bi_bdev = conf->multipaths[mp_bh->path].rdev->bdev;
7b6d91dae   Christoph Hellwig   block: unify flag...
366
  			bio->bi_rw |= REQ_FAILFAST_TRANSPORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
372
373
  			bio->bi_end_io = multipath_end_request;
  			bio->bi_private = mp_bh;
  			generic_make_request(bio);
  		}
  	}
  	spin_unlock_irqrestore(&conf->device_lock, flags);
  }
fd01b88c7   NeilBrown   md: remove typede...
374
  static sector_t multipath_size(struct mddev *mddev, sector_t sectors, int raid_disks)
80c3a6ce4   Dan Williams   md: add 'size' as...
375
376
377
378
379
380
381
  {
  	WARN_ONCE(sectors || raid_disks,
  		  "%s does not support generic reshape
  ", __func__);
  
  	return mddev->dev_sectors;
  }
fd01b88c7   NeilBrown   md: remove typede...
382
  static int multipath_run (struct mddev *mddev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  {
69724e28c   NeilBrown   md/multipath: typ...
384
  	struct mpconf *conf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
  	int disk_idx;
  	struct multipath_info *disk;
3cb030020   NeilBrown   md: removing type...
387
  	struct md_rdev *rdev;
92f861a72   NeilBrown   md/multipath: dis...
388
  	int working_disks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389

0894cc306   Andre Noll   md: Move check fo...
390
391
  	if (md_check_no_bitmap(mddev))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
397
398
399
400
401
402
  	if (mddev->level != LEVEL_MULTIPATH) {
  		printk("multipath: %s: raid level not set to multipath IO (%d)
  ",
  		       mdname(mddev), mddev->level);
  		goto out;
  	}
  	/*
  	 * copy the already verified devices into our private MULTIPATH
  	 * bookkeeping area. [whatever we allocate in multipath_run(),
  	 * should be freed in multipath_stop()]
  	 */
69724e28c   NeilBrown   md/multipath: typ...
403
  	conf = kzalloc(sizeof(struct mpconf), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
408
409
410
411
  	mddev->private = conf;
  	if (!conf) {
  		printk(KERN_ERR 
  			"multipath: couldn't allocate memory for %s
  ",
  			mdname(mddev));
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412

9ffae0cf3   NeilBrown   [PATCH] md: conve...
413
  	conf->multipaths = kzalloc(sizeof(struct multipath_info)*mddev->raid_disks,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
421
  				   GFP_KERNEL);
  	if (!conf->multipaths) {
  		printk(KERN_ERR 
  			"multipath: couldn't allocate memory for %s
  ",
  			mdname(mddev));
  		goto out_free_conf;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

92f861a72   NeilBrown   md/multipath: dis...
423
  	working_disks = 0;
159ec1fc0   Cheng Renquan   md: use list_for_...
424
  	list_for_each_entry(rdev, &mddev->disks, same_set) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
429
430
431
  		disk_idx = rdev->raid_disk;
  		if (disk_idx < 0 ||
  		    disk_idx >= mddev->raid_disks)
  			continue;
  
  		disk = conf->multipaths + disk_idx;
  		disk->rdev = rdev;
8f6c2e4b3   Martin K. Petersen   md: Use new topol...
432
433
  		disk_stack_limits(mddev->gendisk, rdev->bdev,
  				  rdev->data_offset << 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
  		/* as we don't honour merge_bvec_fn, we must never risk
  		 * violating it, not that we ever expect a device with
  		 * a merge_bvec_fn to be involved in multipath */
627a2d3c2   NeilBrown   md: deal with mer...
438
439
440
441
442
  		if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
  			blk_queue_max_segments(mddev->queue, 1);
  			blk_queue_segment_boundary(mddev->queue,
  						   PAGE_CACHE_SIZE - 1);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443

b2d444d7a   NeilBrown   [PATCH] md: conve...
444
  		if (!test_bit(Faulty, &rdev->flags))
92f861a72   NeilBrown   md/multipath: dis...
445
  			working_disks++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
  	}
  
  	conf->raid_disks = mddev->raid_disks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
  	conf->mddev = mddev;
  	spin_lock_init(&conf->device_lock);
  	INIT_LIST_HEAD(&conf->retry_list);
92f861a72   NeilBrown   md/multipath: dis...
452
  	if (!working_disks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
457
  		printk(KERN_ERR "multipath: no operational IO paths for %s
  ",
  			mdname(mddev));
  		goto out_free_conf;
  	}
92f861a72   NeilBrown   md/multipath: dis...
458
  	mddev->degraded = conf->raid_disks - working_disks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459

bbba809e9   Sage Weil   md: avoid use of ...
460
  	conf->pool = mempool_create_kmalloc_pool(NR_RESERVED_BUFS,
26b6e051b   Matthew Dobson   [PATCH] mempool: ...
461
  						 sizeof(struct multipath_bh));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
467
468
469
470
  	if (conf->pool == NULL) {
  		printk(KERN_ERR 
  			"multipath: couldn't allocate memory for %s
  ",
  			mdname(mddev));
  		goto out_free_conf;
  	}
  
  	{
0da3c6194   NeilBrown   md: Improve name ...
471
  		mddev->thread = md_register_thread(multipathd, mddev, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
478
479
480
481
482
  		if (!mddev->thread) {
  			printk(KERN_ERR "multipath: couldn't allocate thread"
  				" for %s
  ", mdname(mddev));
  			goto out_free_conf;
  		}
  	}
  
  	printk(KERN_INFO 
  		"multipath: array %s active with %d out of %d IO paths
  ",
92f861a72   NeilBrown   md/multipath: dis...
483
484
  		mdname(mddev), conf->raid_disks - mddev->degraded,
  	       mddev->raid_disks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
  	/*
  	 * Ok, everything is just fine now
  	 */
1f403624b   Dan Williams   md: centralize ->...
488
  	md_set_array_sectors(mddev, multipath_size(mddev, 0, 0));
7a5febe9f   NeilBrown   [PATCH] md: set t...
489

0d1292282   NeilBrown   [PATCH] md: defin...
490
491
  	mddev->queue->backing_dev_info.congested_fn = multipath_congested;
  	mddev->queue->backing_dev_info.congested_data = mddev;
a91a2785b   Martin K. Petersen   block: Require su...
492
493
494
  
  	if (md_integrity_register(mddev))
  		goto out_free_conf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
  	return 0;
  
  out_free_conf:
  	if (conf->pool)
  		mempool_destroy(conf->pool);
990a8baf5   Jesper Juhl   [PATCH] md: remov...
500
  	kfree(conf->multipaths);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
  	kfree(conf);
  	mddev->private = NULL;
  out:
  	return -EIO;
  }
fd01b88c7   NeilBrown   md: remove typede...
506
  static int multipath_stop (struct mddev *mddev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  {
69724e28c   NeilBrown   md/multipath: typ...
508
  	struct mpconf *conf = mddev->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509

01f96c0a9   NeilBrown   md: Avoid waking ...
510
  	md_unregister_thread(&mddev->thread);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
514
515
516
517
  	blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
  	mempool_destroy(conf->pool);
  	kfree(conf->multipaths);
  	kfree(conf);
  	mddev->private = NULL;
  	return 0;
  }
84fc4b56d   NeilBrown   md: rename "mdk_p...
518
  static struct md_personality multipath_personality =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
  {
  	.name		= "multipath",
2604b703b   NeilBrown   [PATCH] md: remov...
521
  	.level		= LEVEL_MULTIPATH,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
525
526
527
528
529
  	.owner		= THIS_MODULE,
  	.make_request	= multipath_make_request,
  	.run		= multipath_run,
  	.stop		= multipath_stop,
  	.status		= multipath_status,
  	.error_handler	= multipath_error,
  	.hot_add_disk	= multipath_add_disk,
  	.hot_remove_disk= multipath_remove_disk,
80c3a6ce4   Dan Williams   md: add 'size' as...
530
  	.size		= multipath_size,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
  };
  
  static int __init multipath_init (void)
  {
2604b703b   NeilBrown   [PATCH] md: remov...
535
  	return register_md_personality (&multipath_personality);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
538
539
  }
  
  static void __exit multipath_exit (void)
  {
2604b703b   NeilBrown   [PATCH] md: remov...
540
  	unregister_md_personality (&multipath_personality);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
544
545
  }
  
  module_init(multipath_init);
  module_exit(multipath_exit);
  MODULE_LICENSE("GPL");
0efb9e619   NeilBrown   md: add MODULE_DE...
546
  MODULE_DESCRIPTION("simple multi-path personality for MD");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  MODULE_ALIAS("md-personality-7"); /* MULTIPATH */
d9d166c2a   NeilBrown   [PATCH] md: allow...
548
  MODULE_ALIAS("md-multipath");
2604b703b   NeilBrown   [PATCH] md: remov...
549
  MODULE_ALIAS("md-level--4");