Blame view

drivers/md/dm-mpath.c 39 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
  /*
   * Copyright (C) 2003 Sistina Software Limited.
   * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
   *
   * This file is released under the GPL.
   */
586e80e6e   Mikulas Patocka   dm: remove dm hea...
7
  #include <linux/device-mapper.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
  #include "dm-path-selector.h"
b15546f94   Mike Anderson   dm mpath: send ue...
9
  #include "dm-uevent.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
  
  #include <linux/ctype.h>
  #include <linux/init.h>
  #include <linux/mempool.h>
  #include <linux/module.h>
  #include <linux/pagemap.h>
  #include <linux/slab.h>
  #include <linux/time.h>
  #include <linux/workqueue.h>
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
19
  #include <scsi/scsi_dh.h>
60063497a   Arun Sharma   atomic: use <linu...
20
  #include <linux/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21

72d948616   Alasdair G Kergon   [PATCH] dm: impro...
22
  #define DM_MSG_PREFIX "multipath"
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
23
24
  #define DM_PG_INIT_DELAY_MSECS 2000
  #define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
  
  /* Path properties */
  struct pgpath {
  	struct list_head list;
  
  	struct priority_group *pg;	/* Owning PG */
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
31
  	unsigned is_active;		/* Path status */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  	unsigned fail_count;		/* Cumulative failure count */
c922d5f7f   Josef "Jeff" Sipek   [PATCH] struct pa...
33
  	struct dm_path path;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
34
  	struct delayed_work activate_path;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  };
  
  #define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
  
  /*
   * Paths are grouped into Priority Groups and numbered from 1 upwards.
   * Each has a path selector which controls which path gets used.
   */
  struct priority_group {
  	struct list_head list;
  
  	struct multipath *m;		/* Owning multipath instance */
  	struct path_selector ps;
  
  	unsigned pg_num;		/* Reference number */
  	unsigned bypassed;		/* Temporarily bypass this PG? */
  
  	unsigned nr_pgpaths;		/* Number of paths in PG */
  	struct list_head pgpaths;
  };
  
  /* Multipath context */
  struct multipath {
  	struct list_head list;
  	struct dm_target *ti;
  
  	spinlock_t lock;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
62
  	const char *hw_handler_name;
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
63
  	char *hw_handler_params;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
64

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
  	unsigned nr_priority_groups;
  	struct list_head priority_groups;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
67
68
  
  	wait_queue_head_t pg_init_wait;	/* Wait for pg_init completion */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  	unsigned pg_init_required;	/* pg_init needs calling? */
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
70
  	unsigned pg_init_in_progress;	/* Only one pg_init allowed at once */
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
71
  	unsigned pg_init_delay_retry;	/* Delay pg_init retry? */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
  
  	unsigned nr_valid_paths;	/* Total number of usable paths */
  	struct pgpath *current_pgpath;
  	struct priority_group *current_pg;
  	struct priority_group *next_pg;	/* Switch to this PG if set */
  	unsigned repeat_count;		/* I/Os left before calling PS again */
  
  	unsigned queue_io;		/* Must we queue all I/O? */
  	unsigned queue_if_no_path;	/* Queue I/O if last path fails? */
436d41087   Alasdair G Kergon   [PATCH] device-ma...
81
  	unsigned saved_queue_if_no_path;/* Saved state during suspension */
c9e45581a   Dave Wysochanski   dm mpath: add ret...
82
83
  	unsigned pg_init_retries;	/* Number of times to retry pg_init */
  	unsigned pg_init_count;		/* Number of times pg_init called */
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
84
  	unsigned pg_init_delay_msecs;	/* Number of msecs before pg_init retry */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  
  	struct work_struct process_queued_ios;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
87
  	struct list_head queued_ios;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
92
  	unsigned queue_size;
  
  	struct work_struct trigger_event;
  
  	/*
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
93
  	 * We must use a mempool of dm_mpath_io structs so that we
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
  	 * can resubmit bios on error.
  	 */
  	mempool_t *mpio_pool;
6380f26f0   Mike Anderson   dm mpath: add mut...
97
98
  
  	struct mutex work_mutex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
  };
  
  /*
   * Context information attached to each bio we process.
   */
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
104
  struct dm_mpath_io {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  	struct pgpath *pgpath;
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
106
  	size_t nr_bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
  };
  
  typedef int (*action_fn) (struct pgpath *pgpath);
  
  #define MIN_IOS 256	/* Mempool size */
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
112
  static struct kmem_cache *_mpio_cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113

bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
114
  static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
c4028958b   David Howells   WorkStruct: make ...
115
116
  static void process_queued_ios(struct work_struct *work);
  static void trigger_event(struct work_struct *work);
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
117
  static void activate_path(struct work_struct *work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
  
  
  /*-----------------------------------------------
   * Allocation routines
   *-----------------------------------------------*/
  
  static struct pgpath *alloc_pgpath(void)
  {
e69fae561   Micha³ Miros³aw   [PATCH] dm mpath:...
126
  	struct pgpath *pgpath = kzalloc(sizeof(*pgpath), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127

224cb3e98   Mike Anderson   dm: Call blk_abor...
128
  	if (pgpath) {
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
129
  		pgpath->is_active = 1;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
130
  		INIT_DELAYED_WORK(&pgpath->activate_path, activate_path);
224cb3e98   Mike Anderson   dm: Call blk_abor...
131
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  
  	return pgpath;
  }
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
135
  static void free_pgpath(struct pgpath *pgpath)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
  {
  	kfree(pgpath);
  }
  
  static struct priority_group *alloc_priority_group(void)
  {
  	struct priority_group *pg;
e69fae561   Micha³ Miros³aw   [PATCH] dm mpath:...
143
  	pg = kzalloc(sizeof(*pg), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144

e69fae561   Micha³ Miros³aw   [PATCH] dm mpath:...
145
146
  	if (pg)
  		INIT_LIST_HEAD(&pg->pgpaths);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
  
  	return pg;
  }
  
  static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
  {
  	struct pgpath *pgpath, *tmp;
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
154
  	struct multipath *m = ti->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
  
  	list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
  		list_del(&pgpath->list);
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
158
159
  		if (m->hw_handler_name)
  			scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  		dm_put_device(ti, pgpath->path.dev);
  		free_pgpath(pgpath);
  	}
  }
  
  static void free_priority_group(struct priority_group *pg,
  				struct dm_target *ti)
  {
  	struct path_selector *ps = &pg->ps;
  
  	if (ps->type) {
  		ps->type->destroy(ps);
  		dm_put_path_selector(ps->type);
  	}
  
  	free_pgpaths(&pg->pgpaths, ti);
  	kfree(pg);
  }
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
178
  static struct multipath *alloc_multipath(struct dm_target *ti)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
  {
  	struct multipath *m;
e69fae561   Micha³ Miros³aw   [PATCH] dm mpath:...
181
  	m = kzalloc(sizeof(*m), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  	if (m) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  		INIT_LIST_HEAD(&m->priority_groups);
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
184
  		INIT_LIST_HEAD(&m->queued_ios);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
  		spin_lock_init(&m->lock);
  		m->queue_io = 1;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
187
  		m->pg_init_delay_msecs = DM_PG_INIT_DELAY_DEFAULT;
c4028958b   David Howells   WorkStruct: make ...
188
189
  		INIT_WORK(&m->process_queued_ios, process_queued_ios);
  		INIT_WORK(&m->trigger_event, trigger_event);
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
190
  		init_waitqueue_head(&m->pg_init_wait);
6380f26f0   Mike Anderson   dm mpath: add mut...
191
  		mutex_init(&m->work_mutex);
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
192
  		m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
  		if (!m->mpio_pool) {
  			kfree(m);
  			return NULL;
  		}
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
197
198
  		m->ti = ti;
  		ti->private = m;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
206
  	}
  
  	return m;
  }
  
  static void free_multipath(struct multipath *m)
  {
  	struct priority_group *pg, *tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
211
  
  	list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
  		list_del(&pg->list);
  		free_priority_group(pg, m->ti);
  	}
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
212
  	kfree(m->hw_handler_name);
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
213
  	kfree(m->hw_handler_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
221
  	mempool_destroy(m->mpio_pool);
  	kfree(m);
  }
  
  
  /*-----------------------------------------------
   * Path selection
   *-----------------------------------------------*/
fb6126429   Kiyoshi Ueda   dm mpath: refacto...
222
223
224
  static void __pg_init_all_paths(struct multipath *m)
  {
  	struct pgpath *pgpath;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
225
  	unsigned long pg_init_delay = 0;
fb6126429   Kiyoshi Ueda   dm mpath: refacto...
226
227
228
  
  	m->pg_init_count++;
  	m->pg_init_required = 0;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
229
230
231
  	if (m->pg_init_delay_retry)
  		pg_init_delay = msecs_to_jiffies(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT ?
  						 m->pg_init_delay_msecs : DM_PG_INIT_DELAY_MSECS);
fb6126429   Kiyoshi Ueda   dm mpath: refacto...
232
233
234
235
  	list_for_each_entry(pgpath, &m->current_pg->pgpaths, list) {
  		/* Skip failed paths */
  		if (!pgpath->is_active)
  			continue;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
236
237
  		if (queue_delayed_work(kmpath_handlerd, &pgpath->activate_path,
  				       pg_init_delay))
fb6126429   Kiyoshi Ueda   dm mpath: refacto...
238
239
240
  			m->pg_init_in_progress++;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
  static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
  	m->current_pg = pgpath->pg;
  
  	/* Must we initialise the PG first, and queue I/O till it's ready? */
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
246
  	if (m->hw_handler_name) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
251
252
  		m->pg_init_required = 1;
  		m->queue_io = 1;
  	} else {
  		m->pg_init_required = 0;
  		m->queue_io = 0;
  	}
c9e45581a   Dave Wysochanski   dm mpath: add ret...
253
254
  
  	m->pg_init_count = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  }
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
256
257
  static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg,
  			       size_t nr_bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  {
c922d5f7f   Josef "Jeff" Sipek   [PATCH] struct pa...
259
  	struct dm_path *path;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
261
  	path = pg->ps.type->select_path(&pg->ps, &m->repeat_count, nr_bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
269
270
271
  	if (!path)
  		return -ENXIO;
  
  	m->current_pgpath = path_to_pgpath(path);
  
  	if (m->current_pg != pg)
  		__switch_pg(m, m->current_pgpath);
  
  	return 0;
  }
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
272
  static void __choose_pgpath(struct multipath *m, size_t nr_bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
280
281
282
283
  {
  	struct priority_group *pg;
  	unsigned bypassed = 1;
  
  	if (!m->nr_valid_paths)
  		goto failed;
  
  	/* Were we instructed to switch PG? */
  	if (m->next_pg) {
  		pg = m->next_pg;
  		m->next_pg = NULL;
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
284
  		if (!__choose_path_in_pg(m, pg, nr_bytes))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
  			return;
  	}
  
  	/* Don't change PG until it has no remaining paths */
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
289
  	if (m->current_pg && !__choose_path_in_pg(m, m->current_pg, nr_bytes))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
296
297
298
299
300
  		return;
  
  	/*
  	 * Loop through priority groups until we find a valid path.
  	 * First time we skip PGs marked 'bypassed'.
  	 * Second time we only try the ones we skipped.
  	 */
  	do {
  		list_for_each_entry(pg, &m->priority_groups, list) {
  			if (pg->bypassed == bypassed)
  				continue;
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
301
  			if (!__choose_path_in_pg(m, pg, nr_bytes))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
  				return;
  		}
  	} while (bypassed--);
  
  failed:
  	m->current_pgpath = NULL;
  	m->current_pg = NULL;
  }
45e157206   Kiyoshi Ueda   [PATCH] dm: mpath...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  /*
   * Check whether bios must be queued in the device-mapper core rather
   * than here in the target.
   *
   * m->lock must be held on entry.
   *
   * If m->queue_if_no_path and m->saved_queue_if_no_path hold the
   * same value then we are not between multipath_presuspend()
   * and multipath_resume() calls and we have no need to check
   * for the DMF_NOFLUSH_SUSPENDING flag.
   */
  static int __must_push_back(struct multipath *m)
  {
  	return (m->queue_if_no_path != m->saved_queue_if_no_path &&
  		dm_noflush_suspending(m->ti));
  }
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
326
  static int map_io(struct multipath *m, struct request *clone,
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
327
  		  struct dm_mpath_io *mpio, unsigned was_queued)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  {
d2a7ad29a   Kiyoshi Ueda   [PATCH] dm: map a...
329
  	int r = DM_MAPIO_REMAPPED;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
330
  	size_t nr_bytes = blk_rq_bytes(clone);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  	unsigned long flags;
  	struct pgpath *pgpath;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
333
  	struct block_device *bdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	/* Do we need to select a new pgpath? */
  	if (!m->current_pgpath ||
  	    (!m->queue_io && (m->repeat_count && --m->repeat_count == 0)))
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
340
  		__choose_pgpath(m, nr_bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
347
  
  	pgpath = m->current_pgpath;
  
  	if (was_queued)
  		m->queue_size--;
  
  	if ((pgpath && m->queue_io) ||
436d41087   Alasdair G Kergon   [PATCH] device-ma...
348
  	    (!pgpath && m->queue_if_no_path)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  		/* Queue for the daemon to resubmit */
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
350
  		list_add_tail(&clone->queuelist, &m->queued_ios);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  		m->queue_size++;
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
352
353
  		if ((m->pg_init_required && !m->pg_init_in_progress) ||
  		    !m->queue_io)
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
354
  			queue_work(kmultipathd, &m->process_queued_ios);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  		pgpath = NULL;
d2a7ad29a   Kiyoshi Ueda   [PATCH] dm: map a...
356
  		r = DM_MAPIO_SUBMITTED;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
357
358
359
360
361
  	} else if (pgpath) {
  		bdev = pgpath->path.dev->bdev;
  		clone->q = bdev_get_queue(bdev);
  		clone->rq_disk = bdev->bd_disk;
  	} else if (__must_push_back(m))
45e157206   Kiyoshi Ueda   [PATCH] dm: mpath...
362
363
364
  		r = DM_MAPIO_REQUEUE;
  	else
  		r = -EIO;	/* Failed */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
  
  	mpio->pgpath = pgpath;
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
367
368
369
370
371
  	mpio->nr_bytes = nr_bytes;
  
  	if (r == DM_MAPIO_REMAPPED && pgpath->pg->ps.type->start_io)
  		pgpath->pg->ps.type->start_io(&pgpath->pg->ps, &pgpath->path,
  					      nr_bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
378
379
380
  
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return r;
  }
  
  /*
   * If we run out of usable paths, should we queue I/O or error it?
   */
485ef69ed   Alasdair G Kergon   [PATCH] device-ma...
381
382
  static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path,
  			    unsigned save_old_value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&m->lock, flags);
485ef69ed   Alasdair G Kergon   [PATCH] device-ma...
387
388
389
390
  	if (save_old_value)
  		m->saved_queue_if_no_path = m->queue_if_no_path;
  	else
  		m->saved_queue_if_no_path = queue_if_no_path;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  	m->queue_if_no_path = queue_if_no_path;
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
392
  	if (!m->queue_if_no_path && m->queue_size)
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
393
  		queue_work(kmultipathd, &m->process_queued_ios);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return 0;
  }
  
  /*-----------------------------------------------------------------
   * The multipath daemon is responsible for resubmitting queued ios.
   *---------------------------------------------------------------*/
  
  static void dispatch_queued_ios(struct multipath *m)
  {
  	int r;
  	unsigned long flags;
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
408
  	struct dm_mpath_io *mpio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	union map_info *info;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
410
411
  	struct request *clone, *n;
  	LIST_HEAD(cl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
  
  	spin_lock_irqsave(&m->lock, flags);
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
414
  	list_splice_init(&m->queued_ios, &cl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
  	spin_unlock_irqrestore(&m->lock, flags);
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
416
417
  	list_for_each_entry_safe(clone, n, &cl, queuelist) {
  		list_del_init(&clone->queuelist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
419
  		info = dm_get_rq_mapinfo(clone);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  		mpio = info->ptr;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
421
422
423
424
425
426
427
428
429
430
  		r = map_io(m, clone, mpio, 1);
  		if (r < 0) {
  			mempool_free(mpio, m->mpio_pool);
  			dm_kill_unmapped_request(clone, r);
  		} else if (r == DM_MAPIO_REMAPPED)
  			dm_dispatch_request(clone);
  		else if (r == DM_MAPIO_REQUEUE) {
  			mempool_free(mpio, m->mpio_pool);
  			dm_requeue_unmapped_request(clone);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
  	}
  }
c4028958b   David Howells   WorkStruct: make ...
433
  static void process_queued_ios(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  {
c4028958b   David Howells   WorkStruct: make ...
435
436
  	struct multipath *m =
  		container_of(work, struct multipath, process_queued_ios);
fb6126429   Kiyoshi Ueda   dm mpath: refacto...
437
  	struct pgpath *pgpath = NULL;
e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
438
  	unsigned must_queue = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
441
  	unsigned long flags;
  
  	spin_lock_irqsave(&m->lock, flags);
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
442
443
  	if (!m->queue_size)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  	if (!m->current_pgpath)
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
445
  		__choose_pgpath(m, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
  
  	pgpath = m->current_pgpath;
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
448
449
450
  	if ((pgpath && !m->queue_io) ||
  	    (!pgpath && !m->queue_if_no_path))
  		must_queue = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451

fb6126429   Kiyoshi Ueda   dm mpath: refacto...
452
453
  	if (m->pg_init_required && !m->pg_init_in_progress && pgpath)
  		__pg_init_all_paths(m);
c3cd4f6b2   Alasdair G Kergon   [PATCH] device-ma...
454
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  	spin_unlock_irqrestore(&m->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
457
458
459
460
461
462
463
  	if (!must_queue)
  		dispatch_queued_ios(m);
  }
  
  /*
   * An event is triggered whenever a path is taken out of use.
   * Includes path failure and PG bypass.
   */
c4028958b   David Howells   WorkStruct: make ...
464
  static void trigger_event(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  {
c4028958b   David Howells   WorkStruct: make ...
466
467
  	struct multipath *m =
  		container_of(work, struct multipath, trigger_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
472
473
474
475
476
477
478
479
480
481
  
  	dm_table_event(m->ti->table);
  }
  
  /*-----------------------------------------------------------------
   * Constructor/argument parsing:
   * <#multipath feature args> [<arg>]*
   * <#hw_handler args> [hw_handler [<arg>]*]
   * <#priority groups>
   * <initial priority group>
   *     [<selector> <#selector args> [<arg>]*
   *      <#paths> <#per-path selector args>
   *         [<path> [<arg>]* ]+ ]+
   *---------------------------------------------------------------*/
498f0103e   Mike Snitzer   dm table: share t...
482
  static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
486
487
  			       struct dm_target *ti)
  {
  	int r;
  	struct path_selector_type *pst;
  	unsigned ps_argc;
498f0103e   Mike Snitzer   dm table: share t...
488
  	static struct dm_arg _args[] = {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
489
  		{0, 1024, "invalid number of path selector args"},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  	};
498f0103e   Mike Snitzer   dm table: share t...
491
  	pst = dm_get_path_selector(dm_shift_arg(as));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
  	if (!pst) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
493
  		ti->error = "unknown path selector type";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
  		return -EINVAL;
  	}
498f0103e   Mike Snitzer   dm table: share t...
496
  	r = dm_read_arg_group(_args, as, &ps_argc, &ti->error);
371b2e348   Mikulas Patocka   dm mpath: free pa...
497
498
  	if (r) {
  		dm_put_path_selector(pst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
  		return -EINVAL;
371b2e348   Mikulas Patocka   dm mpath: free pa...
500
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
  
  	r = pst->create(&pg->ps, ps_argc, as->argv);
  	if (r) {
  		dm_put_path_selector(pst);
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
505
  		ti->error = "path selector constructor failed";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
  		return r;
  	}
  
  	pg->ps.type = pst;
498f0103e   Mike Snitzer   dm table: share t...
510
  	dm_consume_args(as, ps_argc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
  
  	return 0;
  }
498f0103e   Mike Snitzer   dm table: share t...
514
  static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
  			       struct dm_target *ti)
  {
  	int r;
  	struct pgpath *p;
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
519
  	struct multipath *m = ti->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
  
  	/* we need at least a path arg */
  	if (as->argc < 1) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
523
  		ti->error = "no device given";
01460f352   Benjamin Marzinski   dm mpath: use mor...
524
  		return ERR_PTR(-EINVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
  	}
  
  	p = alloc_pgpath();
  	if (!p)
01460f352   Benjamin Marzinski   dm mpath: use mor...
529
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530

498f0103e   Mike Snitzer   dm table: share t...
531
  	r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
8215d6ec5   Nikanth Karthikesan   dm table: remove ...
532
  			  &p->path.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  	if (r) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
534
  		ti->error = "error getting device";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  		goto bad;
  	}
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
537
  	if (m->hw_handler_name) {
a0cf7ea95   Hannes Reinecke   dm mpath: change ...
538
539
540
541
542
543
544
545
546
547
548
  		struct request_queue *q = bdev_get_queue(p->path.dev->bdev);
  
  		r = scsi_dh_attach(q, m->hw_handler_name);
  		if (r == -EBUSY) {
  			/*
  			 * Already attached to different hw_handler,
  			 * try to reattach with correct one.
  			 */
  			scsi_dh_detach(q);
  			r = scsi_dh_attach(q, m->hw_handler_name);
  		}
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
549
  		if (r < 0) {
a0cf7ea95   Hannes Reinecke   dm mpath: change ...
550
  			ti->error = "error attaching hardware handler";
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
551
552
553
  			dm_put_device(ti, p->path.dev);
  			goto bad;
  		}
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
554
555
556
557
558
559
560
561
562
563
564
  
  		if (m->hw_handler_params) {
  			r = scsi_dh_set_params(q, m->hw_handler_params);
  			if (r < 0) {
  				ti->error = "unable to set hardware "
  							"handler parameters";
  				scsi_dh_detach(q);
  				dm_put_device(ti, p->path.dev);
  				goto bad;
  			}
  		}
ae11b1b36   Hannes Reinecke   [SCSI] scsi_dh: a...
565
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
570
571
572
573
574
575
  	r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error);
  	if (r) {
  		dm_put_device(ti, p->path.dev);
  		goto bad;
  	}
  
  	return p;
  
   bad:
  	free_pgpath(p);
01460f352   Benjamin Marzinski   dm mpath: use mor...
576
  	return ERR_PTR(r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
  }
498f0103e   Mike Snitzer   dm table: share t...
578
  static struct priority_group *parse_priority_group(struct dm_arg_set *as,
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
579
  						   struct multipath *m)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
  {
498f0103e   Mike Snitzer   dm table: share t...
581
  	static struct dm_arg _args[] = {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
582
583
  		{1, 1024, "invalid number of paths"},
  		{0, 1024, "invalid number of selector args"}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
  	};
  
  	int r;
498f0103e   Mike Snitzer   dm table: share t...
587
  	unsigned i, nr_selector_args, nr_args;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  	struct priority_group *pg;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
589
  	struct dm_target *ti = m->ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
  
  	if (as->argc < 2) {
  		as->argc = 0;
01460f352   Benjamin Marzinski   dm mpath: use mor...
593
594
  		ti->error = "not enough priority group arguments";
  		return ERR_PTR(-EINVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
  	}
  
  	pg = alloc_priority_group();
  	if (!pg) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
599
  		ti->error = "couldn't allocate priority group";
01460f352   Benjamin Marzinski   dm mpath: use mor...
600
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
604
605
606
607
608
609
610
  	}
  	pg->m = m;
  
  	r = parse_path_selector(as, pg, ti);
  	if (r)
  		goto bad;
  
  	/*
  	 * read the paths
  	 */
498f0103e   Mike Snitzer   dm table: share t...
611
  	r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
  	if (r)
  		goto bad;
498f0103e   Mike Snitzer   dm table: share t...
614
  	r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
  	if (r)
  		goto bad;
498f0103e   Mike Snitzer   dm table: share t...
617
  	nr_args = 1 + nr_selector_args;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
  	for (i = 0; i < pg->nr_pgpaths; i++) {
  		struct pgpath *pgpath;
498f0103e   Mike Snitzer   dm table: share t...
620
  		struct dm_arg_set path_args;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621

498f0103e   Mike Snitzer   dm table: share t...
622
  		if (as->argc < nr_args) {
148acff61   Mikulas Patocka   dm mpath: return ...
623
  			ti->error = "not enough path parameters";
6bbf79a14   Alasdair G Kergon   dm mpath: fix NUL...
624
  			r = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
  			goto bad;
148acff61   Mikulas Patocka   dm mpath: return ...
626
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627

498f0103e   Mike Snitzer   dm table: share t...
628
  		path_args.argc = nr_args;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
  		path_args.argv = as->argv;
  
  		pgpath = parse_path(&path_args, &pg->ps, ti);
01460f352   Benjamin Marzinski   dm mpath: use mor...
632
633
  		if (IS_ERR(pgpath)) {
  			r = PTR_ERR(pgpath);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  			goto bad;
01460f352   Benjamin Marzinski   dm mpath: use mor...
635
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
  
  		pgpath->pg = pg;
  		list_add_tail(&pgpath->list, &pg->pgpaths);
498f0103e   Mike Snitzer   dm table: share t...
639
  		dm_consume_args(as, nr_args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
  	}
  
  	return pg;
  
   bad:
  	free_priority_group(pg, ti);
01460f352   Benjamin Marzinski   dm mpath: use mor...
646
  	return ERR_PTR(r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  }
498f0103e   Mike Snitzer   dm table: share t...
648
  static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
  	unsigned hw_argc;
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
651
  	int ret;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
652
  	struct dm_target *ti = m->ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653

498f0103e   Mike Snitzer   dm table: share t...
654
  	static struct dm_arg _args[] = {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
655
  		{0, 1024, "invalid number of hardware handler args"},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
  	};
498f0103e   Mike Snitzer   dm table: share t...
657
  	if (dm_read_arg_group(_args, as, &hw_argc, &ti->error))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
661
  		return -EINVAL;
  
  	if (!hw_argc)
  		return 0;
498f0103e   Mike Snitzer   dm table: share t...
662
  	m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
663
664
  	request_module("scsi_dh_%s", m->hw_handler_name);
  	if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
665
  		ti->error = "unknown hardware handler type";
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
666
667
  		ret = -EINVAL;
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
  	}
14e98c5ca   Chandra Seetharaman   dm mpath: warn if...
669

2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  	if (hw_argc > 1) {
  		char *p;
  		int i, j, len = 4;
  
  		for (i = 0; i <= hw_argc - 2; i++)
  			len += strlen(as->argv[i]) + 1;
  		p = m->hw_handler_params = kzalloc(len, GFP_KERNEL);
  		if (!p) {
  			ti->error = "memory allocation failed";
  			ret = -ENOMEM;
  			goto fail;
  		}
  		j = sprintf(p, "%d", hw_argc - 1);
  		for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
  			j = sprintf(p, "%s", as->argv[i]);
  	}
498f0103e   Mike Snitzer   dm table: share t...
686
  	dm_consume_args(as, hw_argc - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
688
  
  	return 0;
2bfd2e133   Chandra Seetharaman   [SCSI] scsi_dh: U...
689
690
691
692
  fail:
  	kfree(m->hw_handler_name);
  	m->hw_handler_name = NULL;
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
  }
498f0103e   Mike Snitzer   dm table: share t...
694
  static int parse_features(struct dm_arg_set *as, struct multipath *m)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
  {
  	int r;
  	unsigned argc;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
698
  	struct dm_target *ti = m->ti;
498f0103e   Mike Snitzer   dm table: share t...
699
  	const char *arg_name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700

498f0103e   Mike Snitzer   dm table: share t...
701
  	static struct dm_arg _args[] = {
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
702
  		{0, 5, "invalid number of feature args"},
c9e45581a   Dave Wysochanski   dm mpath: add ret...
703
  		{1, 50, "pg_init_retries must be between 1 and 50"},
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
704
  		{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
  	};
498f0103e   Mike Snitzer   dm table: share t...
706
  	r = dm_read_arg_group(_args, as, &argc, &ti->error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
  	if (r)
  		return -EINVAL;
  
  	if (!argc)
  		return 0;
c9e45581a   Dave Wysochanski   dm mpath: add ret...
712
  	do {
498f0103e   Mike Snitzer   dm table: share t...
713
  		arg_name = dm_shift_arg(as);
c9e45581a   Dave Wysochanski   dm mpath: add ret...
714
  		argc--;
498f0103e   Mike Snitzer   dm table: share t...
715
  		if (!strcasecmp(arg_name, "queue_if_no_path")) {
c9e45581a   Dave Wysochanski   dm mpath: add ret...
716
717
718
  			r = queue_if_no_path(m, 1, 0);
  			continue;
  		}
498f0103e   Mike Snitzer   dm table: share t...
719
  		if (!strcasecmp(arg_name, "pg_init_retries") &&
c9e45581a   Dave Wysochanski   dm mpath: add ret...
720
  		    (argc >= 1)) {
498f0103e   Mike Snitzer   dm table: share t...
721
  			r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
c9e45581a   Dave Wysochanski   dm mpath: add ret...
722
723
724
  			argc--;
  			continue;
  		}
498f0103e   Mike Snitzer   dm table: share t...
725
  		if (!strcasecmp(arg_name, "pg_init_delay_msecs") &&
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
726
  		    (argc >= 1)) {
498f0103e   Mike Snitzer   dm table: share t...
727
  			r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error);
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
728
729
730
  			argc--;
  			continue;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
  		ti->error = "Unrecognised multipath feature request";
c9e45581a   Dave Wysochanski   dm mpath: add ret...
732
733
734
735
  		r = -EINVAL;
  	} while (argc && !r);
  
  	return r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
  }
  
  static int multipath_ctr(struct dm_target *ti, unsigned int argc,
  			 char **argv)
  {
498f0103e   Mike Snitzer   dm table: share t...
741
742
  	/* target arguments */
  	static struct dm_arg _args[] = {
a490a07a6   Mike Snitzer   dm mpath: allow t...
743
744
  		{0, 1024, "invalid number of priority groups"},
  		{0, 1024, "invalid initial priority group number"},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
  	};
  
  	int r;
  	struct multipath *m;
498f0103e   Mike Snitzer   dm table: share t...
749
  	struct dm_arg_set as;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
752
753
754
  	unsigned pg_count = 0;
  	unsigned next_pg_num;
  
  	as.argc = argc;
  	as.argv = argv;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
755
  	m = alloc_multipath(ti);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
  	if (!m) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
757
  		ti->error = "can't allocate multipath";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
  		return -EINVAL;
  	}
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
760
  	r = parse_features(&as, m);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
  	if (r)
  		goto bad;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
763
  	r = parse_hw_handler(&as, m);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
765
  	if (r)
  		goto bad;
498f0103e   Mike Snitzer   dm table: share t...
766
  	r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
  	if (r)
  		goto bad;
498f0103e   Mike Snitzer   dm table: share t...
769
  	r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
  	if (r)
  		goto bad;
a490a07a6   Mike Snitzer   dm mpath: allow t...
772
773
774
775
776
777
  	if ((!m->nr_priority_groups && next_pg_num) ||
  	    (m->nr_priority_groups && !next_pg_num)) {
  		ti->error = "invalid initial priority group";
  		r = -EINVAL;
  		goto bad;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
  	/* parse the priority groups */
  	while (as.argc) {
  		struct priority_group *pg;
28f16c203   Micha³ Miros³aw   [PATCH] dm mpath:...
781
  		pg = parse_priority_group(&as, m);
01460f352   Benjamin Marzinski   dm mpath: use mor...
782
783
  		if (IS_ERR(pg)) {
  			r = PTR_ERR(pg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
787
788
789
790
791
792
793
794
795
  			goto bad;
  		}
  
  		m->nr_valid_paths += pg->nr_pgpaths;
  		list_add_tail(&pg->list, &m->priority_groups);
  		pg_count++;
  		pg->pg_num = pg_count;
  		if (!--next_pg_num)
  			m->next_pg = pg;
  	}
  
  	if (pg_count != m->nr_priority_groups) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
796
  		ti->error = "priority group count mismatch";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
798
799
  		r = -EINVAL;
  		goto bad;
  	}
8627921fa   Mikulas Patocka   dm mpath: support...
800
  	ti->num_flush_requests = 1;
959eb4e55   Mike Snitzer   dm mpath: support...
801
  	ti->num_discard_requests = 1;
8627921fa   Mikulas Patocka   dm mpath: support...
802

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
805
806
807
808
  	return 0;
  
   bad:
  	free_multipath(m);
  	return r;
  }
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
  static void multipath_wait_for_pg_init_completion(struct multipath *m)
  {
  	DECLARE_WAITQUEUE(wait, current);
  	unsigned long flags;
  
  	add_wait_queue(&m->pg_init_wait, &wait);
  
  	while (1) {
  		set_current_state(TASK_UNINTERRUPTIBLE);
  
  		spin_lock_irqsave(&m->lock, flags);
  		if (!m->pg_init_in_progress) {
  			spin_unlock_irqrestore(&m->lock, flags);
  			break;
  		}
  		spin_unlock_irqrestore(&m->lock, flags);
  
  		io_schedule();
  	}
  	set_current_state(TASK_RUNNING);
  
  	remove_wait_queue(&m->pg_init_wait, &wait);
  }
  
  static void flush_multipath_work(struct multipath *m)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
  {
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
835
  	flush_workqueue(kmpath_handlerd);
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
836
  	multipath_wait_for_pg_init_completion(m);
a044d0168   Alasdair G Kergon   [PATCH] device-ma...
837
  	flush_workqueue(kmultipathd);
d5ffa387e   Tejun Heo   dm: dont use flus...
838
  	flush_work_sync(&m->trigger_event);
6df400ab6   Kiyoshi Ueda   dm mpath: flush w...
839
840
841
842
843
  }
  
  static void multipath_dtr(struct dm_target *ti)
  {
  	struct multipath *m = ti->private;
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
844
  	flush_multipath_work(m);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
847
848
  	free_multipath(m);
  }
  
  /*
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
849
   * Map cloned requests
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
   */
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
851
  static int multipath_map(struct dm_target *ti, struct request *clone,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
854
  			 union map_info *map_context)
  {
  	int r;
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
855
  	struct dm_mpath_io *mpio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  	struct multipath *m = (struct multipath *) ti->private;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
857
858
859
860
861
  	mpio = mempool_alloc(m->mpio_pool, GFP_ATOMIC);
  	if (!mpio)
  		/* ENOMEM, requeue */
  		return DM_MAPIO_REQUEUE;
  	memset(mpio, 0, sizeof(*mpio));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  
  	map_context->ptr = mpio;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
864
865
  	clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
  	r = map_io(m, clone, mpio, 0);
45e157206   Kiyoshi Ueda   [PATCH] dm: mpath...
866
  	if (r < 0 || r == DM_MAPIO_REQUEUE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
869
870
871
872
873
874
875
876
877
878
879
880
  		mempool_free(mpio, m->mpio_pool);
  
  	return r;
  }
  
  /*
   * Take a path out of use.
   */
  static int fail_path(struct pgpath *pgpath)
  {
  	unsigned long flags;
  	struct multipath *m = pgpath->pg->m;
  
  	spin_lock_irqsave(&m->lock, flags);
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
881
  	if (!pgpath->is_active)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
  		goto out;
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
883
  	DMWARN("Failing path %s.", pgpath->path.dev->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
885
  
  	pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
886
  	pgpath->is_active = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
890
891
892
  	pgpath->fail_count++;
  
  	m->nr_valid_paths--;
  
  	if (pgpath == m->current_pgpath)
  		m->current_pgpath = NULL;
b15546f94   Mike Anderson   dm mpath: send ue...
893
894
  	dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti,
  		      pgpath->path.dev->name, m->nr_valid_paths);
fe9cf30eb   Alasdair G Kergon   dm mpath: move tr...
895
  	schedule_work(&m->trigger_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
  
  out:
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return 0;
  }
  
  /*
   * Reinstate a previously-failed path
   */
  static int reinstate_path(struct pgpath *pgpath)
  {
  	int r = 0;
  	unsigned long flags;
  	struct multipath *m = pgpath->pg->m;
  
  	spin_lock_irqsave(&m->lock, flags);
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
913
  	if (pgpath->is_active)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
  		goto out;
def052d21   Alasdair G Kergon   dm mpath: fix tes...
915
  	if (!pgpath->pg->ps.type->reinstate_path) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
917
918
919
920
921
922
923
924
  		DMWARN("Reinstate path not supported by path selector %s",
  		       pgpath->pg->ps.type->name);
  		r = -EINVAL;
  		goto out;
  	}
  
  	r = pgpath->pg->ps.type->reinstate_path(&pgpath->pg->ps, &pgpath->path);
  	if (r)
  		goto out;
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
925
  	pgpath->is_active = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926

e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
927
928
  	if (!m->nr_valid_paths++ && m->queue_size) {
  		m->current_pgpath = NULL;
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
929
  		queue_work(kmultipathd, &m->process_queued_ios);
e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
930
  	} else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
931
  		if (queue_work(kmpath_handlerd, &pgpath->activate_path.work))
e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
932
933
  			m->pg_init_in_progress++;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934

b15546f94   Mike Anderson   dm mpath: send ue...
935
936
  	dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti,
  		      pgpath->path.dev->name, m->nr_valid_paths);
fe9cf30eb   Alasdair G Kergon   dm mpath: move tr...
937
  	schedule_work(&m->trigger_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
939
940
941
942
943
944
945
946
947
948
949
950
  
  out:
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return r;
  }
  
  /*
   * Fail or reinstate all paths that match the provided struct dm_dev.
   */
  static int action_dev(struct multipath *m, struct dm_dev *dev,
  		      action_fn action)
  {
19040c0bc   Mike Snitzer   dm mpath: fail me...
951
  	int r = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
  	struct pgpath *pgpath;
  	struct priority_group *pg;
  
  	list_for_each_entry(pg, &m->priority_groups, list) {
  		list_for_each_entry(pgpath, &pg->pgpaths, list) {
  			if (pgpath->path.dev == dev)
  				r = action(pgpath);
  		}
  	}
  
  	return r;
  }
  
  /*
   * Temporarily try to avoid having to use the specified PG
   */
  static void bypass_pg(struct multipath *m, struct priority_group *pg,
  		      int bypassed)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	pg->bypassed = bypassed;
  	m->current_pgpath = NULL;
  	m->current_pg = NULL;
  
  	spin_unlock_irqrestore(&m->lock, flags);
fe9cf30eb   Alasdair G Kergon   dm mpath: move tr...
980
  	schedule_work(&m->trigger_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
  }
  
  /*
   * Switch to using the specified PG from the next I/O that gets mapped
   */
  static int switch_pg_num(struct multipath *m, const char *pgstr)
  {
  	struct priority_group *pg;
  	unsigned pgnum;
  	unsigned long flags;
  
  	if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
  	    (pgnum > m->nr_priority_groups)) {
  		DMWARN("invalid PG number supplied to switch_pg_num");
  		return -EINVAL;
  	}
  
  	spin_lock_irqsave(&m->lock, flags);
  	list_for_each_entry(pg, &m->priority_groups, list) {
  		pg->bypassed = 0;
  		if (--pgnum)
  			continue;
  
  		m->current_pgpath = NULL;
  		m->current_pg = NULL;
  		m->next_pg = pg;
  	}
  	spin_unlock_irqrestore(&m->lock, flags);
fe9cf30eb   Alasdair G Kergon   dm mpath: move tr...
1009
  	schedule_work(&m->trigger_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  	return 0;
  }
  
  /*
   * Set/clear bypassed status of a PG.
   * PGs are numbered upwards from 1 in the order they were declared.
   */
  static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed)
  {
  	struct priority_group *pg;
  	unsigned pgnum;
  
  	if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
  	    (pgnum > m->nr_priority_groups)) {
  		DMWARN("invalid PG number supplied to bypass_pg");
  		return -EINVAL;
  	}
  
  	list_for_each_entry(pg, &m->priority_groups, list) {
  		if (!--pgnum)
  			break;
  	}
  
  	bypass_pg(m, pg, bypassed);
  	return 0;
  }
  
  /*
c9e45581a   Dave Wysochanski   dm mpath: add ret...
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
   * Should we retry pg_init immediately?
   */
  static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
  {
  	unsigned long flags;
  	int limit_reached = 0;
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	if (m->pg_init_count <= m->pg_init_retries)
  		m->pg_init_required = 1;
  	else
  		limit_reached = 1;
  
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return limit_reached;
  }
3ae31f6a7   Chandra Seetharaman   [SCSI] scsi_dh: C...
1056
  static void pg_init_done(void *data, int errors)
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1057
  {
83c0d5d53   Moger, Babu   dm mpath: pass st...
1058
  	struct pgpath *pgpath = data;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1059
1060
1061
  	struct priority_group *pg = pgpath->pg;
  	struct multipath *m = pg->m;
  	unsigned long flags;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1062
  	unsigned delay_retry = 0;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
  
  	/* device or driver problems */
  	switch (errors) {
  	case SCSI_DH_OK:
  		break;
  	case SCSI_DH_NOSYS:
  		if (!m->hw_handler_name) {
  			errors = 0;
  			break;
  		}
f7b934c81   Moger, Babu   dm mpath: skip ac...
1073
1074
  		DMERR("Could not failover the device: Handler scsi_dh_%s "
  		      "Error %d.", m->hw_handler_name, errors);
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
  		/*
  		 * Fail path for now, so we do not ping pong
  		 */
  		fail_path(pgpath);
  		break;
  	case SCSI_DH_DEV_TEMP_BUSY:
  		/*
  		 * Probably doing something like FW upgrade on the
  		 * controller so try the other pg.
  		 */
  		bypass_pg(m, pg, 1);
  		break;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1087
  	case SCSI_DH_RETRY:
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1088
1089
  		/* Wait before retrying. */
  		delay_retry = 1;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
  	case SCSI_DH_IMM_RETRY:
  	case SCSI_DH_RES_TEMP_UNAVAIL:
  		if (pg_init_limit_reached(m, pgpath))
  			fail_path(pgpath);
  		errors = 0;
  		break;
  	default:
  		/*
  		 * We probably do not want to fail the path for a device
  		 * error, but this is what the old dm did. In future
  		 * patches we can do more advanced handling.
  		 */
  		fail_path(pgpath);
  	}
  
  	spin_lock_irqsave(&m->lock, flags);
  	if (errors) {
e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
1107
1108
1109
1110
1111
  		if (pgpath == m->current_pgpath) {
  			DMERR("Could not failover device. Error %d.", errors);
  			m->current_pgpath = NULL;
  			m->current_pg = NULL;
  		}
d0259bf0e   Kiyoshi Ueda   dm mpath: hold io...
1112
  	} else if (!m->pg_init_required)
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1113
  		pg->bypassed = 0;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1114

d0259bf0e   Kiyoshi Ueda   dm mpath: hold io...
1115
1116
1117
1118
1119
1120
  	if (--m->pg_init_in_progress)
  		/* Activations of other paths are still on going */
  		goto out;
  
  	if (!m->pg_init_required)
  		m->queue_io = 0;
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1121
  	m->pg_init_delay_retry = delay_retry;
d0259bf0e   Kiyoshi Ueda   dm mpath: hold io...
1122
  	queue_work(kmultipathd, &m->process_queued_ios);
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
1123
1124
1125
1126
  	/*
  	 * Wake up any thread waiting to suspend.
  	 */
  	wake_up(&m->pg_init_wait);
d0259bf0e   Kiyoshi Ueda   dm mpath: hold io...
1127
  out:
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1128
1129
  	spin_unlock_irqrestore(&m->lock, flags);
  }
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1130
1131
  static void activate_path(struct work_struct *work)
  {
e54f77ddd   Chandra Seetharaman   dm mpath: call ac...
1132
  	struct pgpath *pgpath =
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1133
  		container_of(work, struct pgpath, activate_path.work);
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1134

3ae31f6a7   Chandra Seetharaman   [SCSI] scsi_dh: C...
1135
  	scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
83c0d5d53   Moger, Babu   dm mpath: pass st...
1136
  				pg_init_done, pgpath);
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1137
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
  /*
   * end_io handling
   */
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1141
  static int do_end_io(struct multipath *m, struct request *clone,
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
1142
  		     int error, struct dm_mpath_io *mpio)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143
  {
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
  	/*
  	 * We don't queue any clone request inside the multipath target
  	 * during end I/O handling, since those clone requests don't have
  	 * bio clones.  If we queue them inside the multipath target,
  	 * we need to make bio clones, that requires memory allocation.
  	 * (See drivers/md/dm.c:end_clone_bio() about why the clone requests
  	 *  don't have bio clones.)
  	 * Instead of queueing the clone request here, we queue the original
  	 * request into dm core, which will remake a clone request and
  	 * clone bios for it and resubmit it later.
  	 */
  	int r = DM_ENDIO_REQUEUE;
640eb3b04   Stefan Bader   [PATCH] device-ma...
1156
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1157

f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1158
  	if (!error && !clone->errors)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1159
  		return 0;	/* I/O complete */
6f13f6fba   Martin K. Petersen   dm mpath: do not ...
1160
  	if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ)
959eb4e55   Mike Snitzer   dm mpath: support...
1161
  		return error;
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1162
1163
  	if (mpio->pgpath)
  		fail_path(mpio->pgpath);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1164

640eb3b04   Stefan Bader   [PATCH] device-ma...
1165
  	spin_lock_irqsave(&m->lock, flags);
751b2a7d6   Hannes Reinecke   [SCSI] dm mpath: ...
1166
1167
1168
1169
1170
1171
1172
1173
1174
  	if (!m->nr_valid_paths) {
  		if (!m->queue_if_no_path) {
  			if (!__must_push_back(m))
  				r = -EIO;
  		} else {
  			if (error == -EBADE)
  				r = error;
  		}
  	}
640eb3b04   Stefan Bader   [PATCH] device-ma...
1175
  	spin_unlock_irqrestore(&m->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176

f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1177
  	return r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
  }
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1179
  static int multipath_end_io(struct dm_target *ti, struct request *clone,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
  			    int error, union map_info *map_context)
  {
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
1182
1183
  	struct multipath *m = ti->private;
  	struct dm_mpath_io *mpio = map_context->ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1184
1185
1186
  	struct pgpath *pgpath = mpio->pgpath;
  	struct path_selector *ps;
  	int r;
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1187
  	r  = do_end_io(m, clone, error, mpio);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1188
1189
1190
  	if (pgpath) {
  		ps = &pgpath->pg->ps;
  		if (ps->type->end_io)
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
1191
  			ps->type->end_io(ps, &pgpath->path, mpio->nr_bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
  	}
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1193
  	mempool_free(mpio, m->mpio_pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
1195
1196
1197
1198
1199
  
  	return r;
  }
  
  /*
   * Suspend can't complete until all the I/O is processed so if
436d41087   Alasdair G Kergon   [PATCH] device-ma...
1200
1201
1202
   * the last path fails we must error any remaining I/O.
   * Note that if the freeze_bdev fails while suspending, the
   * queue_if_no_path state is lost - userspace should reset it.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1203
1204
1205
1206
   */
  static void multipath_presuspend(struct dm_target *ti)
  {
  	struct multipath *m = (struct multipath *) ti->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207

485ef69ed   Alasdair G Kergon   [PATCH] device-ma...
1208
  	queue_if_no_path(m, 0, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1209
  }
6df400ab6   Kiyoshi Ueda   dm mpath: flush w...
1210
1211
  static void multipath_postsuspend(struct dm_target *ti)
  {
6380f26f0   Mike Anderson   dm mpath: add mut...
1212
1213
1214
  	struct multipath *m = ti->private;
  
  	mutex_lock(&m->work_mutex);
2bded7bd7   Kiyoshi Ueda   dm mpath: wait fo...
1215
  	flush_multipath_work(m);
6380f26f0   Mike Anderson   dm mpath: add mut...
1216
  	mutex_unlock(&m->work_mutex);
6df400ab6   Kiyoshi Ueda   dm mpath: flush w...
1217
  }
436d41087   Alasdair G Kergon   [PATCH] device-ma...
1218
1219
1220
  /*
   * Restore the queue_if_no_path setting.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1221
1222
1223
1224
1225
1226
  static void multipath_resume(struct dm_target *ti)
  {
  	struct multipath *m = (struct multipath *) ti->private;
  	unsigned long flags;
  
  	spin_lock_irqsave(&m->lock, flags);
436d41087   Alasdair G Kergon   [PATCH] device-ma...
1227
  	m->queue_if_no_path = m->saved_queue_if_no_path;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
  	spin_unlock_irqrestore(&m->lock, flags);
  }
  
  /*
   * Info output has the following format:
   * num_multipath_feature_args [multipath_feature_args]*
   * num_handler_status_args [handler_status_args]*
   * num_groups init_group_number
   *            [A|D|E num_ps_status_args [ps_status_args]*
   *             num_paths num_selector_args
   *             [path_dev A|F fail_count [selector_args]* ]+ ]+
   *
   * Table output has the following format (identical to the constructor string):
   * num_feature_args [features_args]*
   * num_handler_args hw_handler [hw_handler_args]*
   * num_groups init_group_number
   *     [priority selector-name num_ps_args [ps_args]*
   *      num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
   */
  static int multipath_status(struct dm_target *ti, status_type_t type,
  			    char *result, unsigned int maxlen)
  {
  	int sz = 0;
  	unsigned long flags;
  	struct multipath *m = (struct multipath *) ti->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1253
1254
1255
1256
1257
1258
1259
1260
1261
  	struct priority_group *pg;
  	struct pgpath *p;
  	unsigned pg_num;
  	char state;
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	/* Features */
  	if (type == STATUSTYPE_INFO)
c9e45581a   Dave Wysochanski   dm mpath: add ret...
1262
1263
1264
  		DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
  	else {
  		DMEMIT("%u ", m->queue_if_no_path +
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1265
1266
  			      (m->pg_init_retries > 0) * 2 +
  			      (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2);
c9e45581a   Dave Wysochanski   dm mpath: add ret...
1267
1268
1269
1270
  		if (m->queue_if_no_path)
  			DMEMIT("queue_if_no_path ");
  		if (m->pg_init_retries)
  			DMEMIT("pg_init_retries %u ", m->pg_init_retries);
4e2d19e46   Chandra Seetharaman   dm mpath: delay a...
1271
1272
  		if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
  			DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
c9e45581a   Dave Wysochanski   dm mpath: add ret...
1273
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1274

cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1275
  	if (!m->hw_handler_name || type == STATUSTYPE_INFO)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1276
1277
  		DMEMIT("0 ");
  	else
cfae5c9bb   Chandra Seetharaman   [SCSI] scsi_dh: U...
1278
  		DMEMIT("1 %s ", m->hw_handler_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1279
1280
1281
1282
1283
1284
1285
1286
  
  	DMEMIT("%u ", m->nr_priority_groups);
  
  	if (m->next_pg)
  		pg_num = m->next_pg->pg_num;
  	else if (m->current_pg)
  		pg_num = m->current_pg->pg_num;
  	else
a490a07a6   Mike Snitzer   dm mpath: allow t...
1287
  		pg_num = (m->nr_priority_groups ? 1 : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
  
  	DMEMIT("%u ", pg_num);
  
  	switch (type) {
  	case STATUSTYPE_INFO:
  		list_for_each_entry(pg, &m->priority_groups, list) {
  			if (pg->bypassed)
  				state = 'D';	/* Disabled */
  			else if (pg == m->current_pg)
  				state = 'A';	/* Currently Active */
  			else
  				state = 'E';	/* Enabled */
  
  			DMEMIT("%c ", state);
  
  			if (pg->ps.type->status)
  				sz += pg->ps.type->status(&pg->ps, NULL, type,
  							  result + sz,
  							  maxlen - sz);
  			else
  				DMEMIT("0 ");
  
  			DMEMIT("%u %u ", pg->nr_pgpaths,
  			       pg->ps.type->info_args);
  
  			list_for_each_entry(p, &pg->pgpaths, list) {
  				DMEMIT("%s %s %u ", p->path.dev->name,
6680073d3   Kiyoshi Ueda   dm mpath: remove ...
1315
  				       p->is_active ? "A" : "F",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
  				       p->fail_count);
  				if (pg->ps.type->status)
  					sz += pg->ps.type->status(&pg->ps,
  					      &p->path, type, result + sz,
  					      maxlen - sz);
  			}
  		}
  		break;
  
  	case STATUSTYPE_TABLE:
  		list_for_each_entry(pg, &m->priority_groups, list) {
  			DMEMIT("%s ", pg->ps.type->name);
  
  			if (pg->ps.type->status)
  				sz += pg->ps.type->status(&pg->ps, NULL, type,
  							  result + sz,
  							  maxlen - sz);
  			else
  				DMEMIT("0 ");
  
  			DMEMIT("%u %u ", pg->nr_pgpaths,
  			       pg->ps.type->table_args);
  
  			list_for_each_entry(p, &pg->pgpaths, list) {
  				DMEMIT("%s ", p->path.dev->name);
  				if (pg->ps.type->status)
  					sz += pg->ps.type->status(&pg->ps,
  					      &p->path, type, result + sz,
  					      maxlen - sz);
  			}
  		}
  		break;
  	}
  
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return 0;
  }
  
  static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
  {
6380f26f0   Mike Anderson   dm mpath: add mut...
1357
  	int r = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1358
1359
1360
  	struct dm_dev *dev;
  	struct multipath *m = (struct multipath *) ti->private;
  	action_fn action;
6380f26f0   Mike Anderson   dm mpath: add mut...
1361
  	mutex_lock(&m->work_mutex);
c2f3d24b7   Kiyoshi Ueda   dm mpath: reject ...
1362
1363
1364
1365
  	if (dm_suspended(ti)) {
  		r = -EBUSY;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1366
  	if (argc == 1) {
498f0103e   Mike Snitzer   dm table: share t...
1367
  		if (!strcasecmp(argv[0], "queue_if_no_path")) {
6380f26f0   Mike Anderson   dm mpath: add mut...
1368
1369
  			r = queue_if_no_path(m, 1, 0);
  			goto out;
498f0103e   Mike Snitzer   dm table: share t...
1370
  		} else if (!strcasecmp(argv[0], "fail_if_no_path")) {
6380f26f0   Mike Anderson   dm mpath: add mut...
1371
1372
1373
  			r = queue_if_no_path(m, 0, 0);
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1374
  	}
6380f26f0   Mike Anderson   dm mpath: add mut...
1375
1376
1377
1378
  	if (argc != 2) {
  		DMWARN("Unrecognised multipath message received.");
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1379

498f0103e   Mike Snitzer   dm table: share t...
1380
  	if (!strcasecmp(argv[0], "disable_group")) {
6380f26f0   Mike Anderson   dm mpath: add mut...
1381
1382
  		r = bypass_pg_num(m, argv[1], 1);
  		goto out;
498f0103e   Mike Snitzer   dm table: share t...
1383
  	} else if (!strcasecmp(argv[0], "enable_group")) {
6380f26f0   Mike Anderson   dm mpath: add mut...
1384
1385
  		r = bypass_pg_num(m, argv[1], 0);
  		goto out;
498f0103e   Mike Snitzer   dm table: share t...
1386
  	} else if (!strcasecmp(argv[0], "switch_group")) {
6380f26f0   Mike Anderson   dm mpath: add mut...
1387
1388
  		r = switch_pg_num(m, argv[1]);
  		goto out;
498f0103e   Mike Snitzer   dm table: share t...
1389
  	} else if (!strcasecmp(argv[0], "reinstate_path"))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1390
  		action = reinstate_path;
498f0103e   Mike Snitzer   dm table: share t...
1391
  	else if (!strcasecmp(argv[0], "fail_path"))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1392
  		action = fail_path;
6380f26f0   Mike Anderson   dm mpath: add mut...
1393
1394
1395
1396
  	else {
  		DMWARN("Unrecognised multipath message received.");
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397

8215d6ec5   Nikanth Karthikesan   dm table: remove ...
1398
  	r = dm_get_device(ti, argv[1], dm_table_get_mode(ti->table), &dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
  	if (r) {
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
1400
  		DMWARN("message: error getting device %s",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
  		       argv[1]);
6380f26f0   Mike Anderson   dm mpath: add mut...
1402
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1403
1404
1405
1406
1407
  	}
  
  	r = action_dev(m, dev, action);
  
  	dm_put_device(ti, dev);
6380f26f0   Mike Anderson   dm mpath: add mut...
1408
1409
  out:
  	mutex_unlock(&m->work_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
  	return r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1411
  }
647b3d008   Al Viro   [PATCH] lose unus...
1412
  static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1413
1414
1415
1416
  			   unsigned long arg)
  {
  	struct multipath *m = (struct multipath *) ti->private;
  	struct block_device *bdev = NULL;
633a08b81   Al Viro   [PATCH] introduce...
1417
  	fmode_t mode = 0;
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1418
1419
1420
1421
1422
1423
  	unsigned long flags;
  	int r = 0;
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	if (!m->current_pgpath)
02ab823fd   Kiyoshi Ueda   dm mpath: add sta...
1424
  		__choose_pgpath(m, 0);
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1425

e90dae1f5   Milan Broz   [PATCH] dm: suppo...
1426
  	if (m->current_pgpath) {
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1427
  		bdev = m->current_pgpath->path.dev->bdev;
633a08b81   Al Viro   [PATCH] introduce...
1428
  		mode = m->current_pgpath->path.dev->mode;
e90dae1f5   Milan Broz   [PATCH] dm: suppo...
1429
  	}
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1430
1431
1432
1433
1434
1435
1436
  
  	if (m->queue_io)
  		r = -EAGAIN;
  	else if (!bdev)
  		r = -EIO;
  
  	spin_unlock_irqrestore(&m->lock, flags);
ec8013bed   Paolo Bonzini   dm: do not forwar...
1437
1438
1439
1440
1441
  	/*
  	 * Only pass ioctls through if the device sizes match exactly.
  	 */
  	if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
  		r = scsi_verify_blk_ioctl(NULL, cmd);
633a08b81   Al Viro   [PATCH] introduce...
1442
  	return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1443
  }
af4874e03   Mike Snitzer   dm target:s intro...
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
  static int multipath_iterate_devices(struct dm_target *ti,
  				     iterate_devices_callout_fn fn, void *data)
  {
  	struct multipath *m = ti->private;
  	struct priority_group *pg;
  	struct pgpath *p;
  	int ret = 0;
  
  	list_for_each_entry(pg, &m->priority_groups, list) {
  		list_for_each_entry(p, &pg->pgpaths, list) {
5dea271b6   Mike Snitzer   dm table: pass co...
1454
  			ret = fn(ti, p->path.dev, ti->begin, ti->len, data);
af4874e03   Mike Snitzer   dm target:s intro...
1455
1456
1457
1458
1459
1460
1461
1462
  			if (ret)
  				goto out;
  		}
  	}
  
  out:
  	return ret;
  }
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
  static int __pgpath_busy(struct pgpath *pgpath)
  {
  	struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
  
  	return dm_underlying_device_busy(q);
  }
  
  /*
   * We return "busy", only when we can map I/Os but underlying devices
   * are busy (so even if we map I/Os now, the I/Os will wait on
   * the underlying queue).
   * In other words, if we want to kill I/Os or queue them inside us
   * due to map unavailability, we don't return "busy".  Otherwise,
   * dm core won't give us the I/Os and we can't do what we want.
   */
  static int multipath_busy(struct dm_target *ti)
  {
  	int busy = 0, has_active = 0;
  	struct multipath *m = ti->private;
  	struct priority_group *pg;
  	struct pgpath *pgpath;
  	unsigned long flags;
  
  	spin_lock_irqsave(&m->lock, flags);
  
  	/* Guess which priority_group will be used at next mapping time */
  	if (unlikely(!m->current_pgpath && m->next_pg))
  		pg = m->next_pg;
  	else if (likely(m->current_pg))
  		pg = m->current_pg;
  	else
  		/*
  		 * We don't know which pg will be used at next mapping time.
  		 * We don't call __choose_pgpath() here to avoid to trigger
  		 * pg_init just by busy checking.
  		 * So we don't know whether underlying devices we will be using
  		 * at next mapping time are busy or not. Just try mapping.
  		 */
  		goto out;
  
  	/*
  	 * If there is one non-busy active path at least, the path selector
  	 * will be able to select it. So we consider such a pg as not busy.
  	 */
  	busy = 1;
  	list_for_each_entry(pgpath, &pg->pgpaths, list)
  		if (pgpath->is_active) {
  			has_active = 1;
  
  			if (!__pgpath_busy(pgpath)) {
  				busy = 0;
  				break;
  			}
  		}
  
  	if (!has_active)
  		/*
  		 * No active path in this pg, so this pg won't be used and
  		 * the current_pg will be changed at next mapping time.
  		 * We need to try mapping to determine it.
  		 */
  		busy = 0;
  
  out:
  	spin_unlock_irqrestore(&m->lock, flags);
  
  	return busy;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1531
1532
1533
1534
1535
  /*-----------------------------------------------------------------
   * Module setup
   *---------------------------------------------------------------*/
  static struct target_type multipath_target = {
  	.name = "multipath",
19040c0bc   Mike Snitzer   dm mpath: fail me...
1536
  	.version = {1, 3, 0},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1537
1538
1539
  	.module = THIS_MODULE,
  	.ctr = multipath_ctr,
  	.dtr = multipath_dtr,
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1540
1541
  	.map_rq = multipath_map,
  	.rq_end_io = multipath_end_io,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1542
  	.presuspend = multipath_presuspend,
6df400ab6   Kiyoshi Ueda   dm mpath: flush w...
1543
  	.postsuspend = multipath_postsuspend,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1544
1545
1546
  	.resume = multipath_resume,
  	.status = multipath_status,
  	.message = multipath_message,
9af4aa30b   Milan Broz   [PATCH] dm mpath:...
1547
  	.ioctl  = multipath_ioctl,
af4874e03   Mike Snitzer   dm target:s intro...
1548
  	.iterate_devices = multipath_iterate_devices,
f40c67f0f   Kiyoshi Ueda   dm mpath: change ...
1549
  	.busy = multipath_busy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1550
1551
1552
1553
1554
1555
1556
  };
  
  static int __init dm_multipath_init(void)
  {
  	int r;
  
  	/* allocate a slab for the dm_ios */
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
1557
  	_mpio_cache = KMEM_CACHE(dm_mpath_io, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1558
1559
1560
1561
1562
  	if (!_mpio_cache)
  		return -ENOMEM;
  
  	r = dm_register_target(&multipath_target);
  	if (r < 0) {
0cd331243   Alasdair G Kergon   dm: remove duplic...
1563
  		DMERR("register failed %d", r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1564
1565
1566
  		kmem_cache_destroy(_mpio_cache);
  		return -EINVAL;
  	}
4d4d66ab5   Tejun Heo   dm: convert workq...
1567
  	kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
1568
  	if (!kmultipathd) {
0cd331243   Alasdair G Kergon   dm: remove duplic...
1569
  		DMERR("failed to create workqueue kmpathd");
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
1570
1571
1572
1573
  		dm_unregister_target(&multipath_target);
  		kmem_cache_destroy(_mpio_cache);
  		return -ENOMEM;
  	}
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1574
1575
1576
1577
1578
1579
  	/*
  	 * A separate workqueue is used to handle the device handlers
  	 * to avoid overloading existing workqueue. Overloading the
  	 * old workqueue would also create a bottleneck in the
  	 * path of the storage hardware device activation.
  	 */
4d4d66ab5   Tejun Heo   dm: convert workq...
1580
1581
  	kmpath_handlerd = alloc_ordered_workqueue("kmpath_handlerd",
  						  WQ_MEM_RECLAIM);
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1582
1583
1584
1585
1586
1587
1588
  	if (!kmpath_handlerd) {
  		DMERR("failed to create workqueue kmpath_handlerd");
  		destroy_workqueue(kmultipathd);
  		dm_unregister_target(&multipath_target);
  		kmem_cache_destroy(_mpio_cache);
  		return -ENOMEM;
  	}
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
1589
  	DMINFO("version %u.%u.%u loaded",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1590
1591
1592
1593
1594
1595
1596
1597
  	       multipath_target.version[0], multipath_target.version[1],
  	       multipath_target.version[2]);
  
  	return r;
  }
  
  static void __exit dm_multipath_exit(void)
  {
bab7cfc73   Chandra Seetharaman   [SCSI] scsi_dh: A...
1598
  	destroy_workqueue(kmpath_handlerd);
c557308e1   Alasdair G Kergon   [PATCH] device-ma...
1599
  	destroy_workqueue(kmultipathd);
10d3bd09a   Mikulas Patocka   dm: consolidate t...
1600
  	dm_unregister_target(&multipath_target);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1601
1602
  	kmem_cache_destroy(_mpio_cache);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1603
1604
1605
1606
1607
1608
  module_init(dm_multipath_init);
  module_exit(dm_multipath_exit);
  
  MODULE_DESCRIPTION(DM_NAME " multipath target");
  MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
  MODULE_LICENSE("GPL");