Blame view

drivers/md/dm-kcopyd.c 20.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   * Copyright (C) 2002 Sistina Software (UK) Limited.
373a392bd   Milan Broz   dm kcopyd: update...
3
   * Copyright (C) 2006 Red Hat GmbH
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
   *
   * This file is released under the GPL.
   *
   * Kcopyd provides a simple interface for copying an area of one
   * block-device to one or more other block-devices, with an asynchronous
   * completion notification.
   */
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
11
  #include <linux/types.h>
60063497a   Arun Sharma   atomic: use <linu...
12
  #include <linux/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/blkdev.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
20
21
22
  #include <linux/fs.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/mempool.h>
  #include <linux/module.h>
  #include <linux/pagemap.h>
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
  #include <linux/workqueue.h>
48c9c27b8   Arjan van de Ven   [PATCH] sem2mutex...
23
  #include <linux/mutex.h>
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
24
  #include <linux/delay.h>
586e80e6e   Mikulas Patocka   dm: remove dm hea...
25
  #include <linux/device-mapper.h>
a765e20ee   Alasdair G Kergon   dm: move include ...
26
  #include <linux/dm-kcopyd.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

4cc96131a   Mike Snitzer   dm: move request-...
28
  #include "dm-core.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
30
31
32
  #define SUB_JOB_SIZE	128
  #define SPLIT_COUNT	8
  #define MIN_JOBS	8
5f43ba295   Mikulas Patocka   dm kcopyd: reserv...
33
  #define RESERVE_PAGES	(DIV_ROUND_UP(SUB_JOB_SIZE << SECTOR_SHIFT, PAGE_SIZE))
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
34

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
  /*-----------------------------------------------------------------
   * Each kcopyd client has its own little pool of preallocated
   * pages for kcopyd io.
   *---------------------------------------------------------------*/
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
39
  struct dm_kcopyd_client {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  	struct page_list *pages;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
41
42
  	unsigned nr_reserved_pages;
  	unsigned nr_free_pages;
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
43

373a392bd   Milan Broz   dm kcopyd: update...
44
  	struct dm_io_client *io_client;
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
45
46
  	wait_queue_head_t destroyq;
  	atomic_t nr_jobs;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
47

08d8757a4   Mikulas Patocka   dm kcopyd: privat...
48
  	mempool_t *job_pool;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
49
50
  	struct workqueue_struct *kcopyd_wq;
  	struct work_struct kcopyd_work;
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
51
  	struct dm_kcopyd_throttle *throttle;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
52
  /*
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
53
   * We maintain four lists of jobs:
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
54
55
56
   *
   * i)   jobs waiting for pages
   * ii)  jobs that have pages, and are waiting for the io to be issued.
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
57
58
   * iii) jobs that don't need to do any IO and just run a callback
   * iv) jobs that have completed.
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
59
   *
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
60
   * All four of these are protected by job_lock.
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
61
62
   */
  	spinlock_t job_lock;
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
63
  	struct list_head callback_jobs;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
64
65
66
  	struct list_head complete_jobs;
  	struct list_head io_jobs;
  	struct list_head pages_jobs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  };
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
68
  static struct page_list zero_page_list;
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  static DEFINE_SPINLOCK(throttle_spinlock);
  
  /*
   * IO/IDLE accounting slowly decays after (1 << ACCOUNT_INTERVAL_SHIFT) period.
   * When total_period >= (1 << ACCOUNT_INTERVAL_SHIFT) the counters are divided
   * by 2.
   */
  #define ACCOUNT_INTERVAL_SHIFT		SHIFT_HZ
  
  /*
   * Sleep this number of milliseconds.
   *
   * The value was decided experimentally.
   * Smaller values seem to cause an increased copy rate above the limit.
   * The reason for this is unknown but possibly due to jiffies rounding errors
   * or read/write cache inside the disk.
   */
  #define SLEEP_MSEC			100
  
  /*
   * Maximum number of sleep events. There is a theoretical livelock if more
   * kcopyd clients do work simultaneously which this limit avoids.
   */
  #define MAX_SLEEPS			10
  
  static void io_job_start(struct dm_kcopyd_throttle *t)
  {
  	unsigned throttle, now, difference;
  	int slept = 0, skew;
  
  	if (unlikely(!t))
  		return;
  
  try_again:
  	spin_lock_irq(&throttle_spinlock);
  
  	throttle = ACCESS_ONCE(t->throttle);
  
  	if (likely(throttle >= 100))
  		goto skip_limit;
  
  	now = jiffies;
  	difference = now - t->last_jiffies;
  	t->last_jiffies = now;
  	if (t->num_io_jobs)
  		t->io_period += difference;
  	t->total_period += difference;
  
  	/*
  	 * Maintain sane values if we got a temporary overflow.
  	 */
  	if (unlikely(t->io_period > t->total_period))
  		t->io_period = t->total_period;
  
  	if (unlikely(t->total_period >= (1 << ACCOUNT_INTERVAL_SHIFT))) {
  		int shift = fls(t->total_period >> ACCOUNT_INTERVAL_SHIFT);
  		t->total_period >>= shift;
  		t->io_period >>= shift;
  	}
  
  	skew = t->io_period - throttle * t->total_period / 100;
  
  	if (unlikely(skew > 0) && slept < MAX_SLEEPS) {
  		slept++;
  		spin_unlock_irq(&throttle_spinlock);
  		msleep(SLEEP_MSEC);
  		goto try_again;
  	}
  
  skip_limit:
  	t->num_io_jobs++;
  
  	spin_unlock_irq(&throttle_spinlock);
  }
  
  static void io_job_finish(struct dm_kcopyd_throttle *t)
  {
  	unsigned long flags;
  
  	if (unlikely(!t))
  		return;
  
  	spin_lock_irqsave(&throttle_spinlock, flags);
  
  	t->num_io_jobs--;
  
  	if (likely(ACCESS_ONCE(t->throttle) >= 100))
  		goto skip_limit;
  
  	if (!t->num_io_jobs) {
  		unsigned now, difference;
  
  		now = jiffies;
  		difference = now - t->last_jiffies;
  		t->last_jiffies = now;
  
  		t->io_period += difference;
  		t->total_period += difference;
  
  		/*
  		 * Maintain sane values if we got a temporary overflow.
  		 */
  		if (unlikely(t->io_period > t->total_period))
  			t->io_period = t->total_period;
  	}
  
  skip_limit:
  	spin_unlock_irqrestore(&throttle_spinlock, flags);
  }
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
178
179
180
181
  static void wake(struct dm_kcopyd_client *kc)
  {
  	queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
182
183
184
  /*
   * Obtain one page for the use of kcopyd.
   */
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
185
  static struct page_list *alloc_pl(gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
  {
  	struct page_list *pl;
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
188
  	pl = kmalloc(sizeof(*pl), gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
  	if (!pl)
  		return NULL;
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
191
  	pl->page = alloc_page(gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
196
197
198
199
200
201
202
203
204
  	if (!pl->page) {
  		kfree(pl);
  		return NULL;
  	}
  
  	return pl;
  }
  
  static void free_pl(struct page_list *pl)
  {
  	__free_page(pl->page);
  	kfree(pl);
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
205
206
207
208
209
  /*
   * Add the provided pages to a client's free page list, releasing
   * back to the system any beyond the reserved_pages limit.
   */
  static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
211
  	struct page_list *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
213
214
  	do {
  		next = pl->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
216
217
218
219
220
221
222
  		if (kc->nr_free_pages >= kc->nr_reserved_pages)
  			free_pl(pl);
  		else {
  			pl->next = kc->pages;
  			kc->pages = pl;
  			kc->nr_free_pages++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
224
225
  		pl = next;
  	} while (pl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
227
228
  static int kcopyd_get_pages(struct dm_kcopyd_client *kc,
  			    unsigned int nr, struct page_list **pages)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
230
  	struct page_list *pl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
232
233
234
  	*pages = NULL;
  
  	do {
d0164adc8   Mel Gorman   mm, page_alloc: d...
235
  		pl = alloc_pl(__GFP_NOWARN | __GFP_NORETRY | __GFP_KSWAPD_RECLAIM);
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
236
237
238
239
240
241
242
243
244
245
246
247
248
  		if (unlikely(!pl)) {
  			/* Use reserved pages */
  			pl = kc->pages;
  			if (unlikely(!pl))
  				goto out_of_memory;
  			kc->pages = pl->next;
  			kc->nr_free_pages--;
  		}
  		pl->next = *pages;
  		*pages = pl;
  	} while (--nr);
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
250
251
252
253
  out_of_memory:
  	if (*pages)
  		kcopyd_put_pages(kc, *pages);
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  }
  
  /*
   * These three functions resize the page pool.
   */
  static void drop_pages(struct page_list *pl)
  {
  	struct page_list *next;
  
  	while (pl) {
  		next = pl->next;
  		free_pl(pl);
  		pl = next;
  	}
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
269
270
271
272
  /*
   * Allocate and reserve nr_pages for the use of a specific client.
   */
  static int client_reserve_pages(struct dm_kcopyd_client *kc, unsigned nr_pages)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
274
  	unsigned i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	struct page_list *pl = NULL, *next;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
276
  	for (i = 0; i < nr_pages; i++) {
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
277
  		next = alloc_pl(GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
282
283
284
285
  		if (!next) {
  			if (pl)
  				drop_pages(pl);
  			return -ENOMEM;
  		}
  		next->next = pl;
  		pl = next;
  	}
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
286
  	kc->nr_reserved_pages += nr_pages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  	kcopyd_put_pages(kc, pl);
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
288

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
  	return 0;
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
291
  static void client_free_pages(struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
293
  	BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
  	drop_pages(kc->pages);
  	kc->pages = NULL;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
296
  	kc->nr_free_pages = kc->nr_reserved_pages = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
303
304
  }
  
  /*-----------------------------------------------------------------
   * kcopyd_jobs need to be allocated by the *clients* of kcopyd,
   * for this reason we use a mempool to prevent the client from
   * ever having to do io (which could cause a deadlock).
   *---------------------------------------------------------------*/
  struct kcopyd_job {
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
305
  	struct dm_kcopyd_client *kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
310
311
312
  	struct list_head list;
  	unsigned long flags;
  
  	/*
  	 * Error state of the job.
  	 */
  	int read_err;
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
313
  	unsigned long write_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
  
  	/*
  	 * Either READ or WRITE
  	 */
  	int rw;
22a1ceb1e   Heinz Mauelshagen   dm io: clean inte...
319
  	struct dm_io_region source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
  
  	/*
  	 * The destinations for the transfer.
  	 */
  	unsigned int num_dests;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
325
  	struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
  	struct page_list *pages;
  
  	/*
  	 * Set this to ensure you are notified when the job has
  	 * completed.  'context' is for callback to use.
  	 */
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
333
  	dm_kcopyd_notify_fn fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
  	void *context;
  
  	/*
  	 * These fields are only used if the job has been split
  	 * into more manageable parts.
  	 */
def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
340
  	struct mutex lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
  	atomic_t sub_jobs;
  	sector_t progress;
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
343
  	sector_t write_offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344

c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
345
346
  	struct kcopyd_job *master_job;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
348
  static struct kmem_cache *_job_cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349

945fa4d28   Mikulas Patocka   dm kcopyd: remove...
350
  int __init dm_kcopyd_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  {
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
352
353
354
  	_job_cache = kmem_cache_create("kcopyd_job",
  				sizeof(struct kcopyd_job) * (SPLIT_COUNT + 1),
  				__alignof__(struct kcopyd_job), 0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
  	if (!_job_cache)
  		return -ENOMEM;
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
357
358
  	zero_page_list.next = &zero_page_list;
  	zero_page_list.page = ZERO_PAGE(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
  	return 0;
  }
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
361
  void dm_kcopyd_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  	kmem_cache_destroy(_job_cache);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
367
368
369
370
  	_job_cache = NULL;
  }
  
  /*
   * Functions to push and pop a job onto the head of a given job
   * list.
   */
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  static struct kcopyd_job *pop_io_job(struct list_head *jobs,
  				     struct dm_kcopyd_client *kc)
  {
  	struct kcopyd_job *job;
  
  	/*
  	 * For I/O jobs, pop any read, any write without sequential write
  	 * constraint and sequential writes that are at the right position.
  	 */
  	list_for_each_entry(job, jobs, list) {
  		if (job->rw == READ || !test_bit(DM_KCOPYD_WRITE_SEQ, &job->flags)) {
  			list_del(&job->list);
  			return job;
  		}
  
  		if (job->write_offset == job->master_job->write_offset) {
  			job->master_job->write_offset += job->source.count;
  			list_del(&job->list);
  			return job;
  		}
  	}
  
  	return NULL;
  }
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
395
396
  static struct kcopyd_job *pop(struct list_head *jobs,
  			      struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
  {
  	struct kcopyd_job *job = NULL;
  	unsigned long flags;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
400
  	spin_lock_irqsave(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
  
  	if (!list_empty(jobs)) {
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
403
404
405
406
407
408
  		if (jobs == &kc->io_jobs)
  			job = pop_io_job(jobs, kc);
  		else {
  			job = list_entry(jobs->next, struct kcopyd_job, list);
  			list_del(&job->list);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	}
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
410
  	spin_unlock_irqrestore(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
  
  	return job;
  }
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
414
  static void push(struct list_head *jobs, struct kcopyd_job *job)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
  {
  	unsigned long flags;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
417
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
419
  	spin_lock_irqsave(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  	list_add_tail(&job->list, jobs);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
421
  	spin_unlock_irqrestore(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  }
b673c3a81   Kazuo Ito   dm kcopyd: avoid ...
423
424
425
426
427
428
429
430
431
432
  
  static void push_head(struct list_head *jobs, struct kcopyd_job *job)
  {
  	unsigned long flags;
  	struct dm_kcopyd_client *kc = job->kc;
  
  	spin_lock_irqsave(&kc->job_lock, flags);
  	list_add(&job->list, jobs);
  	spin_unlock_irqrestore(&kc->job_lock, flags);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
445
  /*
   * These three functions process 1 item from the corresponding
   * job list.
   *
   * They return:
   * < 0: error
   *   0: success
   * > 0: can't process yet.
   */
  static int run_complete_job(struct kcopyd_job *job)
  {
  	void *context = job->context;
  	int read_err = job->read_err;
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
446
  	unsigned long write_err = job->write_err;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
447
448
  	dm_kcopyd_notify_fn fn = job->fn;
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449

7f0696539   Mikulas Patocka   dm kcopyd: add dm...
450
  	if (job->pages && job->pages != &zero_page_list)
73830857b   Mikulas Patocka   dm kcopyd: prepar...
451
  		kcopyd_put_pages(kc, job->pages);
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
452
453
454
455
456
457
  	/*
  	 * If this is the master job, the sub jobs have already
  	 * completed so we can free everything.
  	 */
  	if (job->master_job == job)
  		mempool_free(job, kc->job_pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  	fn(read_err, write_err, context);
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
459
460
461
  
  	if (atomic_dec_and_test(&kc->nr_jobs))
  		wake_up(&kc->destroyq);
120130a75   John Pittman   dm kcopyd: avoid ...
462
  	cond_resched();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
  	return 0;
  }
  
  static void complete_io(unsigned long error, void *context)
  {
  	struct kcopyd_job *job = (struct kcopyd_job *) context;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
469
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470

df5d2e908   Mikulas Patocka   dm kcopyd: introd...
471
  	io_job_finish(kc->throttle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  	if (error) {
511116669   Mike Christie   dm: use op_is_wri...
473
  		if (op_is_write(job->rw))
ce503f59a   Jonathan Brassow   [PATCH] dm kcopyd...
474
  			job->write_err |= error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
  		else
  			job->read_err = 1;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
477
  		if (!test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
478
479
  			push(&kc->complete_jobs, job);
  			wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
  			return;
  		}
  	}
511116669   Mike Christie   dm: use op_is_wri...
483
  	if (op_is_write(job->rw))
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
484
  		push(&kc->complete_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
  
  	else {
  		job->rw = WRITE;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
488
  		push(&kc->io_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  	}
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
490
  	wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
493
494
495
496
497
498
499
  }
  
  /*
   * Request io on as many buffer heads as we can currently get for
   * a particular job.
   */
  static int run_io_job(struct kcopyd_job *job)
  {
  	int r;
373a392bd   Milan Broz   dm kcopyd: update...
500
  	struct dm_io_request io_req = {
e6047149d   Mike Christie   dm: use bio op ac...
501
502
  		.bi_op = job->rw,
  		.bi_op_flags = 0,
373a392bd   Milan Broz   dm kcopyd: update...
503
504
  		.mem.type = DM_IO_PAGE_LIST,
  		.mem.ptr.pl = job->pages,
4622afb3f   Mikulas Patocka   dm kcopyd: remove...
505
  		.mem.offset = 0,
373a392bd   Milan Broz   dm kcopyd: update...
506
507
508
509
  		.notify.fn = complete_io,
  		.notify.context = job,
  		.client = job->kc->io_client,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510

b73c67c2c   Damien Le Moal   dm kcopyd: add se...
511
512
513
514
515
516
517
  	/*
  	 * If we need to write sequentially and some reads or writes failed,
  	 * no point in continuing.
  	 */
  	if (test_bit(DM_KCOPYD_WRITE_SEQ, &job->flags) &&
  	    job->master_job->write_err)
  		return -EIO;
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
518
  	io_job_start(job->kc->throttle);
7eaceacca   Jens Axboe   block: remove per...
519
  	if (job->rw == READ)
373a392bd   Milan Broz   dm kcopyd: update...
520
  		r = dm_io(&io_req, 1, &job->source, NULL);
721a9602e   Jens Axboe   block: kill off R...
521
  	else
373a392bd   Milan Broz   dm kcopyd: update...
522
  		r = dm_io(&io_req, job->num_dests, job->dests, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
529
  
  	return r;
  }
  
  static int run_pages_job(struct kcopyd_job *job)
  {
  	int r;
5bf45a3dc   Mikulas Patocka   dm kcopyd: remove...
530
  	unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531

5bf45a3dc   Mikulas Patocka   dm kcopyd: remove...
532
  	r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
  	if (!r) {
  		/* this job is ready for io */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
535
  		push(&job->kc->io_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  		return 0;
  	}
  
  	if (r == -ENOMEM)
  		/* can't complete now */
  		return 1;
  
  	return r;
  }
  
  /*
   * Run through a list for as long as possible.  Returns the count
   * of successful jobs.
   */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
550
551
  static int process_jobs(struct list_head *jobs, struct dm_kcopyd_client *kc,
  			int (*fn) (struct kcopyd_job *))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
  {
  	struct kcopyd_job *job;
  	int r, count = 0;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
555
  	while ((job = pop(jobs, kc))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
  
  		r = fn(job);
  
  		if (r < 0) {
  			/* error this rogue job */
511116669   Mike Christie   dm: use op_is_wri...
561
  			if (op_is_write(job->rw))
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
562
  				job->write_err = (unsigned long) -1L;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
  			else
  				job->read_err = 1;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
565
  			push(&kc->complete_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
570
571
572
573
  			break;
  		}
  
  		if (r > 0) {
  			/*
  			 * We couldn't service this job ATM, so
  			 * push this job back onto the list.
  			 */
b673c3a81   Kazuo Ito   dm kcopyd: avoid ...
574
  			push_head(jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
578
579
580
581
582
583
584
585
586
  			break;
  		}
  
  		count++;
  	}
  
  	return count;
  }
  
  /*
   * kcopyd does this every time it's woken up.
   */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
587
  static void do_work(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  {
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
589
590
  	struct dm_kcopyd_client *kc = container_of(work,
  					struct dm_kcopyd_client, kcopyd_work);
7eaceacca   Jens Axboe   block: remove per...
591
  	struct blk_plug plug;
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
592
  	unsigned long flags;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
593

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
596
597
598
599
600
  	/*
  	 * The order that these are called is *very* important.
  	 * complete jobs can free some pages for pages jobs.
  	 * Pages jobs when successful will jump onto the io jobs
  	 * list.  io jobs call wake when they complete and it all
  	 * starts again.
  	 */
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
601
602
603
  	spin_lock_irqsave(&kc->job_lock, flags);
  	list_splice_tail_init(&kc->callback_jobs, &kc->complete_jobs);
  	spin_unlock_irqrestore(&kc->job_lock, flags);
7eaceacca   Jens Axboe   block: remove per...
604
  	blk_start_plug(&plug);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
605
606
607
  	process_jobs(&kc->complete_jobs, kc, run_complete_job);
  	process_jobs(&kc->pages_jobs, kc, run_pages_job);
  	process_jobs(&kc->io_jobs, kc, run_io_job);
7eaceacca   Jens Axboe   block: remove per...
608
  	blk_finish_plug(&plug);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
612
613
614
615
616
617
  }
  
  /*
   * If we are copying a small region we just dispatch a single job
   * to do the copy, otherwise the io has to be split up into many
   * jobs.
   */
  static void dispatch_job(struct kcopyd_job *job)
  {
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
618
619
  	struct dm_kcopyd_client *kc = job->kc;
  	atomic_inc(&kc->nr_jobs);
9ca170a3c   Mikulas Patocka   dm kcopyd: accept...
620
  	if (unlikely(!job->source.count))
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
621
  		push(&kc->callback_jobs, job);
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
622
623
  	else if (job->pages == &zero_page_list)
  		push(&kc->io_jobs, job);
9ca170a3c   Mikulas Patocka   dm kcopyd: accept...
624
625
  	else
  		push(&kc->pages_jobs, job);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
626
  	wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
  }
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
628
629
  static void segment_complete(int read_err, unsigned long write_err,
  			     void *context)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
  {
  	/* FIXME: tidy this function */
  	sector_t progress = 0;
  	sector_t count = 0;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
634
635
  	struct kcopyd_job *sub_job = (struct kcopyd_job *) context;
  	struct kcopyd_job *job = sub_job->master_job;
73830857b   Mikulas Patocka   dm kcopyd: prepar...
636
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637

def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
638
  	mutex_lock(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
640
641
642
643
644
  
  	/* update the error */
  	if (read_err)
  		job->read_err = 1;
  
  	if (write_err)
ce503f59a   Jonathan Brassow   [PATCH] dm kcopyd...
645
  		job->write_err |= write_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
647
648
649
650
  
  	/*
  	 * Only dispatch more work if there hasn't been an error.
  	 */
  	if ((!job->read_err && !job->write_err) ||
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
651
  	    test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
654
655
656
657
658
659
660
661
  		/* get the next chunk of work */
  		progress = job->progress;
  		count = job->source.count - progress;
  		if (count) {
  			if (count > SUB_JOB_SIZE)
  				count = SUB_JOB_SIZE;
  
  			job->progress += count;
  		}
  	}
def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
662
  	mutex_unlock(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
  
  	if (count) {
  		int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
  
  		*sub_job = *job;
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
668
  		sub_job->write_offset = progress;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
672
673
674
675
676
677
  		sub_job->source.sector += progress;
  		sub_job->source.count = count;
  
  		for (i = 0; i < job->num_dests; i++) {
  			sub_job->dests[i].sector += progress;
  			sub_job->dests[i].count = count;
  		}
  
  		sub_job->fn = segment_complete;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
678
  		sub_job->context = sub_job;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
681
682
683
  		dispatch_job(sub_job);
  
  	} else if (atomic_dec_and_test(&job->sub_jobs)) {
  
  		/*
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
684
685
686
687
688
689
690
  		 * Queue the completion callback to the kcopyd thread.
  		 *
  		 * Some callers assume that all the completions are called
  		 * from a single thread and don't race with each other.
  		 *
  		 * We must not call the callback directly here because this
  		 * code may not be executing in the thread.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  		 */
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
692
693
  		push(&kc->complete_jobs, job);
  		wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
697
  	}
  }
  
  /*
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
698
   * Create some sub jobs to share the work between them.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
   */
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
700
  static void split_job(struct kcopyd_job *master_job)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
  {
  	int i;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
703
  	atomic_inc(&master_job->kc->nr_jobs);
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
704

c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
705
706
707
708
709
  	atomic_set(&master_job->sub_jobs, SPLIT_COUNT);
  	for (i = 0; i < SPLIT_COUNT; i++) {
  		master_job[i + 1].master_job = master_job;
  		segment_complete(0, 0u, &master_job[i + 1]);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
711
712
713
  int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
  		   unsigned int num_dests, struct dm_io_region *dests,
  		   unsigned int flags, dm_kcopyd_notify_fn fn, void *context)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
  {
  	struct kcopyd_job *job;
70d6c400a   Mike Snitzer   dm kcopyd: add WR...
716
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
  
  	/*
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
719
720
  	 * Allocate an array of jobs consisting of one master job
  	 * followed by SPLIT_COUNT sub jobs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
  	 */
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
722
  	job = mempool_alloc(kc->job_pool, GFP_NOIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
730
  
  	/*
  	 * set up for the read.
  	 */
  	job->kc = kc;
  	job->flags = flags;
  	job->read_err = 0;
  	job->write_err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
  
  	job->num_dests = num_dests;
  	memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
  	/*
  	 * If one of the destination is a host-managed zoned block device,
  	 * we need to write sequentially. If one of the destination is a
  	 * host-aware device, then leave it to the caller to choose what to do.
  	 */
  	if (!test_bit(DM_KCOPYD_WRITE_SEQ, &job->flags)) {
  		for (i = 0; i < job->num_dests; i++) {
  			if (bdev_zoned_model(dests[i].bdev) == BLK_ZONED_HM) {
  				set_bit(DM_KCOPYD_WRITE_SEQ, &job->flags);
  				break;
  			}
  		}
  	}
  
  	/*
  	 * If we need to write sequentially, errors cannot be ignored.
  	 */
  	if (test_bit(DM_KCOPYD_WRITE_SEQ, &job->flags) &&
  	    test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags))
  		clear_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags);
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
754
755
756
757
758
759
760
761
  	if (from) {
  		job->source = *from;
  		job->pages = NULL;
  		job->rw = READ;
  	} else {
  		memset(&job->source, 0, sizeof job->source);
  		job->source.count = job->dests[0].count;
  		job->pages = &zero_page_list;
70d6c400a   Mike Snitzer   dm kcopyd: add WR...
762
763
  
  		/*
615ec946a   Christoph Hellwig   dm kcopyd: switch...
764
  		 * Use WRITE ZEROES to optimize zeroing if all dests support it.
70d6c400a   Mike Snitzer   dm kcopyd: add WR...
765
  		 */
615ec946a   Christoph Hellwig   dm kcopyd: switch...
766
  		job->rw = REQ_OP_WRITE_ZEROES;
70d6c400a   Mike Snitzer   dm kcopyd: add WR...
767
  		for (i = 0; i < job->num_dests; i++)
615ec946a   Christoph Hellwig   dm kcopyd: switch...
768
  			if (!bdev_write_zeroes_sectors(job->dests[i].bdev)) {
70d6c400a   Mike Snitzer   dm kcopyd: add WR...
769
770
771
  				job->rw = WRITE;
  				break;
  			}
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
772
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
773
774
775
  
  	job->fn = fn;
  	job->context = context;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
776
  	job->master_job = job;
b73c67c2c   Damien Le Moal   dm kcopyd: add se...
777
  	job->write_offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778

a705a34a5   Mikulas Patocka   dm kcopyd: avoid ...
779
  	if (job->source.count <= SUB_JOB_SIZE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
  		dispatch_job(job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  	else {
def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
782
  		mutex_init(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
784
785
786
787
788
  		job->progress = 0;
  		split_job(job);
  	}
  
  	return 0;
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
789
  EXPORT_SYMBOL(dm_kcopyd_copy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790

7f0696539   Mikulas Patocka   dm kcopyd: add dm...
791
792
793
794
795
796
797
  int dm_kcopyd_zero(struct dm_kcopyd_client *kc,
  		   unsigned num_dests, struct dm_io_region *dests,
  		   unsigned flags, dm_kcopyd_notify_fn fn, void *context)
  {
  	return dm_kcopyd_copy(kc, NULL, num_dests, dests, flags, fn, context);
  }
  EXPORT_SYMBOL(dm_kcopyd_zero);
a6e50b409   Mikulas Patocka   dm snapshot: skip...
798
799
800
801
802
803
804
805
806
807
808
  void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
  				 dm_kcopyd_notify_fn fn, void *context)
  {
  	struct kcopyd_job *job;
  
  	job = mempool_alloc(kc->job_pool, GFP_NOIO);
  
  	memset(job, 0, sizeof(struct kcopyd_job));
  	job->kc = kc;
  	job->fn = fn;
  	job->context = context;
d136f2efd   Alasdair G Kergon   dm kcopyd: fix jo...
809
  	job->master_job = job;
a6e50b409   Mikulas Patocka   dm snapshot: skip...
810
811
812
813
814
815
816
817
818
819
820
821
822
823
  
  	atomic_inc(&kc->nr_jobs);
  
  	return job;
  }
  EXPORT_SYMBOL(dm_kcopyd_prepare_callback);
  
  void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err)
  {
  	struct kcopyd_job *job = j;
  	struct dm_kcopyd_client *kc = job->kc;
  
  	job->read_err = read_err;
  	job->write_err = write_err;
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
824
  	push(&kc->callback_jobs, job);
a6e50b409   Mikulas Patocka   dm snapshot: skip...
825
826
827
  	wake(kc);
  }
  EXPORT_SYMBOL(dm_kcopyd_do_callback);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
830
831
  /*
   * Cancels a kcopyd job, eg. someone might be deactivating a
   * mirror.
   */
0b56306e5   Adrian Bunk   [PATCH] drivers/m...
832
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
834
835
836
837
  int kcopyd_cancel(struct kcopyd_job *job, int block)
  {
  	/* FIXME: finish */
  	return -1;
  }
0b56306e5   Adrian Bunk   [PATCH] drivers/m...
838
  #endif  /*  0  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
  
  /*-----------------------------------------------------------------
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
841
   * Client setup
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
   *---------------------------------------------------------------*/
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
843
  struct dm_kcopyd_client *dm_kcopyd_client_create(struct dm_kcopyd_throttle *throttle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
  {
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
845
  	int r = -ENOMEM;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
846
  	struct dm_kcopyd_client *kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
849
  	if (!kc)
fa34ce730   Mikulas Patocka   dm kcopyd: return...
850
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
852
  	spin_lock_init(&kc->job_lock);
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
853
  	INIT_LIST_HEAD(&kc->callback_jobs);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
854
855
856
  	INIT_LIST_HEAD(&kc->complete_jobs);
  	INIT_LIST_HEAD(&kc->io_jobs);
  	INIT_LIST_HEAD(&kc->pages_jobs);
df5d2e908   Mikulas Patocka   dm kcopyd: introd...
857
  	kc->throttle = throttle;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
858

08d8757a4   Mikulas Patocka   dm kcopyd: privat...
859
  	kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
860
861
  	if (!kc->job_pool)
  		goto bad_slab;
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
862

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
863
  	INIT_WORK(&kc->kcopyd_work, do_work);
670368a8d   Tejun Heo   dm: stop using WQ...
864
  	kc->kcopyd_wq = alloc_workqueue("kcopyd", WQ_MEM_RECLAIM, 0);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
865
866
  	if (!kc->kcopyd_wq)
  		goto bad_workqueue;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
867

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
  	kc->pages = NULL;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
869
  	kc->nr_reserved_pages = kc->nr_free_pages = 0;
5f43ba295   Mikulas Patocka   dm kcopyd: reserv...
870
  	r = client_reserve_pages(kc, RESERVE_PAGES);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
871
872
  	if (r)
  		goto bad_client_pages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873

bda8efec5   Mikulas Patocka   dm io: use fixed ...
874
  	kc->io_client = dm_io_client_create();
373a392bd   Milan Broz   dm kcopyd: update...
875
876
  	if (IS_ERR(kc->io_client)) {
  		r = PTR_ERR(kc->io_client);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
877
  		goto bad_io_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  	}
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
879
880
  	init_waitqueue_head(&kc->destroyq);
  	atomic_set(&kc->nr_jobs, 0);
fa34ce730   Mikulas Patocka   dm kcopyd: return...
881
  	return kc;
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
882
883
884
885
886
887
888
889
890
  
  bad_io_client:
  	client_free_pages(kc);
  bad_client_pages:
  	destroy_workqueue(kc->kcopyd_wq);
  bad_workqueue:
  	mempool_destroy(kc->job_pool);
  bad_slab:
  	kfree(kc);
fa34ce730   Mikulas Patocka   dm kcopyd: return...
891
  	return ERR_PTR(r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
893
  EXPORT_SYMBOL(dm_kcopyd_client_create);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894

eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
895
  void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
  {
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
897
898
  	/* Wait for completion of all jobs submitted by this client. */
  	wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs));
8561f28c8   Nikos Tsironis   dm kcopyd: Fix bu...
899
  	BUG_ON(!list_empty(&kc->callback_jobs));
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
900
901
902
903
  	BUG_ON(!list_empty(&kc->complete_jobs));
  	BUG_ON(!list_empty(&kc->io_jobs));
  	BUG_ON(!list_empty(&kc->pages_jobs));
  	destroy_workqueue(kc->kcopyd_wq);
373a392bd   Milan Broz   dm kcopyd: update...
904
  	dm_io_client_destroy(kc->io_client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
  	client_free_pages(kc);
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
906
  	mempool_destroy(kc->job_pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
  	kfree(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
909
  EXPORT_SYMBOL(dm_kcopyd_client_destroy);