Blame view

drivers/md/dm-kcopyd.c 16 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>
586e80e6e   Mikulas Patocka   dm: remove dm hea...
24
  #include <linux/device-mapper.h>
a765e20ee   Alasdair G Kergon   dm: move include ...
25
  #include <linux/dm-kcopyd.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

22a1ceb1e   Heinz Mauelshagen   dm io: clean inte...
27
  #include "dm.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

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

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

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

08d8757a4   Mikulas Patocka   dm kcopyd: privat...
47
  	mempool_t *job_pool;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  	struct workqueue_struct *kcopyd_wq;
  	struct work_struct kcopyd_work;
  
  /*
   * We maintain three lists of jobs:
   *
   * i)   jobs waiting for pages
   * ii)  jobs that have pages, and are waiting for the io to be issued.
   * iii) jobs that have completed.
   *
   * All three of these are protected by job_lock.
   */
  	spinlock_t job_lock;
  	struct list_head complete_jobs;
  	struct list_head io_jobs;
  	struct list_head pages_jobs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  };
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
65
  static struct page_list zero_page_list;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
66
67
68
69
  static void wake(struct dm_kcopyd_client *kc)
  {
  	queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
70
71
72
  /*
   * Obtain one page for the use of kcopyd.
   */
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
73
  static struct page_list *alloc_pl(gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  {
  	struct page_list *pl;
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
76
  	pl = kmalloc(sizeof(*pl), gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  	if (!pl)
  		return NULL;
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
79
  	pl->page = alloc_page(gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
89
90
91
92
  	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 ...
93
94
95
96
97
  /*
   * 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
98
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
99
  	struct page_list *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
101
102
  	do {
  		next = pl->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
104
105
106
107
108
109
110
  		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
111

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
112
113
  		pl = next;
  	} while (pl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  }
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
115
116
  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
117
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
118
  	struct page_list *pl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  	*pages = NULL;
  
  	do {
  		pl = alloc_pl(__GFP_NOWARN | __GFP_NORETRY);
  		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
137

d04714580   Mikulas Patocka   dm kcopyd: alloc ...
138
139
140
141
  out_of_memory:
  	if (*pages)
  		kcopyd_put_pages(kc, *pages);
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  }
  
  /*
   * 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 ...
157
158
159
160
  /*
   * 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
161
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
162
  	unsigned i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	struct page_list *pl = NULL, *next;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
164
  	for (i = 0; i < nr_pages; i++) {
f99b55eec   Mikulas Patocka   dm kcopyd: add gf...
165
  		next = alloc_pl(GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
  		if (!next) {
  			if (pl)
  				drop_pages(pl);
  			return -ENOMEM;
  		}
  		next->next = pl;
  		pl = next;
  	}
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
174
  	kc->nr_reserved_pages += nr_pages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	kcopyd_put_pages(kc, pl);
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
176

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
  	return 0;
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
179
  static void client_free_pages(struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  {
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
181
  	BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  	drop_pages(kc->pages);
  	kc->pages = NULL;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
184
  	kc->nr_free_pages = kc->nr_reserved_pages = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
  }
  
  /*-----------------------------------------------------------------
   * 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 ...
193
  	struct dm_kcopyd_client *kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
200
  	struct list_head list;
  	unsigned long flags;
  
  	/*
  	 * Error state of the job.
  	 */
  	int read_err;
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
201
  	unsigned long write_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
  
  	/*
  	 * Either READ or WRITE
  	 */
  	int rw;
22a1ceb1e   Heinz Mauelshagen   dm io: clean inte...
207
  	struct dm_io_region source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
  
  	/*
  	 * The destinations for the transfer.
  	 */
  	unsigned int num_dests;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
213
  	struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
220
  	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 ...
221
  	dm_kcopyd_notify_fn fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
  	void *context;
  
  	/*
  	 * These fields are only used if the job has been split
  	 * into more manageable parts.
  	 */
def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
228
  	struct mutex lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
  	atomic_t sub_jobs;
  	sector_t progress;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231

c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
232
233
  	struct kcopyd_job *master_job;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234

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

945fa4d28   Mikulas Patocka   dm kcopyd: remove...
237
  int __init dm_kcopyd_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  {
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
239
240
241
  	_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
242
243
  	if (!_job_cache)
  		return -ENOMEM;
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
244
245
  	zero_page_list.next = &zero_page_list;
  	zero_page_list.page = ZERO_PAGE(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
  	return 0;
  }
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
248
  void dm_kcopyd_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	kmem_cache_destroy(_job_cache);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
  	_job_cache = NULL;
  }
  
  /*
   * Functions to push and pop a job onto the head of a given job
   * list.
   */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
258
259
  static struct kcopyd_job *pop(struct list_head *jobs,
  			      struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
  {
  	struct kcopyd_job *job = NULL;
  	unsigned long flags;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
263
  	spin_lock_irqsave(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
  
  	if (!list_empty(jobs)) {
  		job = list_entry(jobs->next, struct kcopyd_job, list);
  		list_del(&job->list);
  	}
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
269
  	spin_unlock_irqrestore(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
  
  	return job;
  }
028867ac2   Alasdair G Kergon   dm: use kmem_cach...
273
  static void push(struct list_head *jobs, struct kcopyd_job *job)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
  {
  	unsigned long flags;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
276
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
278
  	spin_lock_irqsave(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  	list_add_tail(&job->list, jobs);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
280
  	spin_unlock_irqrestore(&kc->job_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  }
b673c3a81   Kazuo Ito   dm kcopyd: avoid ...
282
283
284
285
286
287
288
289
290
291
  
  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
292
293
294
295
296
297
298
299
300
301
302
303
304
  /*
   * 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...
305
  	unsigned long write_err = job->write_err;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
306
307
  	dm_kcopyd_notify_fn fn = job->fn;
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308

7f0696539   Mikulas Patocka   dm kcopyd: add dm...
309
  	if (job->pages && job->pages != &zero_page_list)
73830857b   Mikulas Patocka   dm kcopyd: prepar...
310
  		kcopyd_put_pages(kc, job->pages);
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
311
312
313
314
315
316
  	/*
  	 * 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
317
  	fn(read_err, write_err, context);
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
318
319
320
  
  	if (atomic_dec_and_test(&kc->nr_jobs))
  		wake_up(&kc->destroyq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
  	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...
327
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
  
  	if (error) {
  		if (job->rw == WRITE)
ce503f59a   Jonathan Brassow   [PATCH] dm kcopyd...
331
  			job->write_err |= error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
  		else
  			job->read_err = 1;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
334
  		if (!test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
335
336
  			push(&kc->complete_jobs, job);
  			wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
  			return;
  		}
  	}
  
  	if (job->rw == WRITE)
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
342
  		push(&kc->complete_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
  
  	else {
  		job->rw = WRITE;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
346
  		push(&kc->io_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  	}
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
348
  	wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
351
352
353
354
355
356
357
  }
  
  /*
   * 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...
358
  	struct dm_io_request io_req = {
8d35d3e37   Mikulas Patocka   dm kcopyd: delay ...
359
  		.bi_rw = job->rw,
373a392bd   Milan Broz   dm kcopyd: update...
360
361
  		.mem.type = DM_IO_PAGE_LIST,
  		.mem.ptr.pl = job->pages,
4622afb3f   Mikulas Patocka   dm kcopyd: remove...
362
  		.mem.offset = 0,
373a392bd   Milan Broz   dm kcopyd: update...
363
364
365
366
  		.notify.fn = complete_io,
  		.notify.context = job,
  		.client = job->kc->io_client,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367

7eaceacca   Jens Axboe   block: remove per...
368
  	if (job->rw == READ)
373a392bd   Milan Broz   dm kcopyd: update...
369
  		r = dm_io(&io_req, 1, &job->source, NULL);
721a9602e   Jens Axboe   block: kill off R...
370
  	else
373a392bd   Milan Broz   dm kcopyd: update...
371
  		r = dm_io(&io_req, job->num_dests, job->dests, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
378
  
  	return r;
  }
  
  static int run_pages_job(struct kcopyd_job *job)
  {
  	int r;
5bf45a3dc   Mikulas Patocka   dm kcopyd: remove...
379
  	unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380

5bf45a3dc   Mikulas Patocka   dm kcopyd: remove...
381
  	r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  	if (!r) {
  		/* this job is ready for io */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
384
  		push(&job->kc->io_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
390
391
392
393
394
395
396
397
398
  		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...
399
400
  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
401
402
403
  {
  	struct kcopyd_job *job;
  	int r, count = 0;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
404
  	while ((job = pop(jobs, kc))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
  
  		r = fn(job);
  
  		if (r < 0) {
  			/* error this rogue job */
  			if (job->rw == WRITE)
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
411
  				job->write_err = (unsigned long) -1L;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
  			else
  				job->read_err = 1;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
414
  			push(&kc->complete_jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
  			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 ...
423
  			push_head(jobs, job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
427
428
429
430
431
432
433
434
435
  			break;
  		}
  
  		count++;
  	}
  
  	return count;
  }
  
  /*
   * kcopyd does this every time it's woken up.
   */
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
436
  static void do_work(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  {
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
438
439
  	struct dm_kcopyd_client *kc = container_of(work,
  					struct dm_kcopyd_client, kcopyd_work);
7eaceacca   Jens Axboe   block: remove per...
440
  	struct blk_plug plug;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
441

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
445
446
447
448
  	/*
  	 * 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.
  	 */
7eaceacca   Jens Axboe   block: remove per...
449
  	blk_start_plug(&plug);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
450
451
452
  	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...
453
  	blk_finish_plug(&plug);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
461
462
  }
  
  /*
   * 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...
463
464
  	struct dm_kcopyd_client *kc = job->kc;
  	atomic_inc(&kc->nr_jobs);
9ca170a3c   Mikulas Patocka   dm kcopyd: accept...
465
466
  	if (unlikely(!job->source.count))
  		push(&kc->complete_jobs, job);
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
467
468
  	else if (job->pages == &zero_page_list)
  		push(&kc->io_jobs, job);
9ca170a3c   Mikulas Patocka   dm kcopyd: accept...
469
470
  	else
  		push(&kc->pages_jobs, job);
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
471
  	wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  }
4cdc1d1fa   Alasdair G Kergon   dm io: write erro...
473
474
  static void segment_complete(int read_err, unsigned long write_err,
  			     void *context)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
  {
  	/* FIXME: tidy this function */
  	sector_t progress = 0;
  	sector_t count = 0;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
479
480
  	struct kcopyd_job *sub_job = (struct kcopyd_job *) context;
  	struct kcopyd_job *job = sub_job->master_job;
73830857b   Mikulas Patocka   dm kcopyd: prepar...
481
  	struct dm_kcopyd_client *kc = job->kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482

def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
483
  	mutex_lock(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
487
488
489
  
  	/* update the error */
  	if (read_err)
  		job->read_err = 1;
  
  	if (write_err)
ce503f59a   Jonathan Brassow   [PATCH] dm kcopyd...
490
  		job->write_err |= write_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
493
494
495
  
  	/*
  	 * Only dispatch more work if there hasn't been an error.
  	 */
  	if ((!job->read_err && !job->write_err) ||
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
496
  	    test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
499
500
501
502
503
504
505
506
  		/* 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 ...
507
  	mutex_unlock(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
  
  	if (count) {
  		int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
514
515
516
517
518
519
520
521
  
  		*sub_job = *job;
  		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...
522
  		sub_job->context = sub_job;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
  		dispatch_job(sub_job);
  
  	} else if (atomic_dec_and_test(&job->sub_jobs)) {
  
  		/*
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
528
529
530
531
532
533
534
  		 * 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
535
  		 */
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
536
537
  		push(&kc->complete_jobs, job);
  		wake(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
540
541
  	}
  }
  
  /*
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
542
   * Create some sub jobs to share the work between them.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
   */
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
544
  static void split_job(struct kcopyd_job *master_job)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
  {
  	int i;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
547
  	atomic_inc(&master_job->kc->nr_jobs);
340cd4445   Mikulas Patocka   dm kcopyd: fix ca...
548

c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
549
550
551
552
553
  	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
554
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
555
556
557
  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
558
559
560
561
  {
  	struct kcopyd_job *job;
  
  	/*
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
562
563
  	 * Allocate an array of jobs consisting of one master job
  	 * followed by SPLIT_COUNT sub jobs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
  	 */
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
565
  	job = mempool_alloc(kc->job_pool, GFP_NOIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
570
571
572
573
  
  	/*
  	 * 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
574
575
576
  
  	job->num_dests = num_dests;
  	memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
7f0696539   Mikulas Patocka   dm kcopyd: add dm...
577
578
579
580
581
582
583
584
585
586
  	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;
  		job->rw = WRITE;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
  
  	job->fn = fn;
  	job->context = context;
c6ea41fbb   Mikulas Patocka   dm kcopyd: preall...
590
  	job->master_job = job;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591

a705a34a5   Mikulas Patocka   dm kcopyd: avoid ...
592
  	if (job->source.count <= SUB_JOB_SIZE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  		dispatch_job(job);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
  	else {
def5b5b26   Matthias Kaehlcke   kcopyd use mutex ...
595
  		mutex_init(&job->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
601
  		job->progress = 0;
  		split_job(job);
  	}
  
  	return 0;
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
602
  EXPORT_SYMBOL(dm_kcopyd_copy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603

7f0696539   Mikulas Patocka   dm kcopyd: add dm...
604
605
606
607
608
609
610
  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...
611
612
613
614
615
616
617
618
619
620
621
  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...
622
  	job->master_job = job;
a6e50b409   Mikulas Patocka   dm snapshot: skip...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
  
  	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;
  
  	push(&kc->complete_jobs, job);
  	wake(kc);
  }
  EXPORT_SYMBOL(dm_kcopyd_do_callback);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
  /*
   * Cancels a kcopyd job, eg. someone might be deactivating a
   * mirror.
   */
0b56306e5   Adrian Bunk   [PATCH] drivers/m...
646
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
  int kcopyd_cancel(struct kcopyd_job *job, int block)
  {
  	/* FIXME: finish */
  	return -1;
  }
0b56306e5   Adrian Bunk   [PATCH] drivers/m...
652
  #endif  /*  0  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
  
  /*-----------------------------------------------------------------
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
655
   * Client setup
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
   *---------------------------------------------------------------*/
fa34ce730   Mikulas Patocka   dm kcopyd: return...
657
  struct dm_kcopyd_client *dm_kcopyd_client_create(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  {
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
659
  	int r = -ENOMEM;
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
660
  	struct dm_kcopyd_client *kc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661

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

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
666
667
668
669
  	spin_lock_init(&kc->job_lock);
  	INIT_LIST_HEAD(&kc->complete_jobs);
  	INIT_LIST_HEAD(&kc->io_jobs);
  	INIT_LIST_HEAD(&kc->pages_jobs);
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
670
  	kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
671
672
  	if (!kc->job_pool)
  		goto bad_slab;
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
673

8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
674
  	INIT_WORK(&kc->kcopyd_work, do_work);
9c4376de9   Tejun Heo   dm: use non reent...
675
676
  	kc->kcopyd_wq = alloc_workqueue("kcopyd",
  					WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
677
678
  	if (!kc->kcopyd_wq)
  		goto bad_workqueue;
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
679

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
  	kc->pages = NULL;
d04714580   Mikulas Patocka   dm kcopyd: alloc ...
681
  	kc->nr_reserved_pages = kc->nr_free_pages = 0;
5f43ba295   Mikulas Patocka   dm kcopyd: reserv...
682
  	r = client_reserve_pages(kc, RESERVE_PAGES);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
683
684
  	if (r)
  		goto bad_client_pages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685

bda8efec5   Mikulas Patocka   dm io: use fixed ...
686
  	kc->io_client = dm_io_client_create();
373a392bd   Milan Broz   dm kcopyd: update...
687
688
  	if (IS_ERR(kc->io_client)) {
  		r = PTR_ERR(kc->io_client);
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
689
  		goto bad_io_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  	}
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
691
692
  	init_waitqueue_head(&kc->destroyq);
  	atomic_set(&kc->nr_jobs, 0);
fa34ce730   Mikulas Patocka   dm kcopyd: return...
693
  	return kc;
945fa4d28   Mikulas Patocka   dm kcopyd: remove...
694
695
696
697
698
699
700
701
702
  
  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...
703
  	return ERR_PTR(r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
705
  EXPORT_SYMBOL(dm_kcopyd_client_create);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706

eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
707
  void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
  {
138728dc9   Alasdair G Kergon   [PATCH] dm snapsh...
709
710
  	/* Wait for completion of all jobs submitted by this client. */
  	wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs));
8c0cbc2f7   Mikulas Patocka   dm kcopyd: per de...
711
712
713
714
  	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...
715
  	dm_io_client_destroy(kc->io_client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  	client_free_pages(kc);
08d8757a4   Mikulas Patocka   dm kcopyd: privat...
717
  	mempool_destroy(kc->job_pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  	kfree(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
  }
eb69aca5d   Heinz Mauelshagen   dm kcopyd: clean ...
720
  EXPORT_SYMBOL(dm_kcopyd_client_destroy);