Blame view
drivers/md/dm-kcopyd.c
16 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 |
/* * Copyright (C) 2002 Sistina Software (UK) Limited. |
373a392bd dm kcopyd: update... |
3 |
* Copyright (C) 2006 Red Hat GmbH |
1da177e4c 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 dm kcopyd: clean ... |
11 |
#include <linux/types.h> |
60063497a atomic: use <linu... |
12 |
#include <linux/atomic.h> |
1da177e4c Linux-2.6.12-rc2 |
13 |
#include <linux/blkdev.h> |
1da177e4c 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 [PATCH] sem2mutex... |
23 |
#include <linux/mutex.h> |
586e80e6e dm: remove dm hea... |
24 |
#include <linux/device-mapper.h> |
a765e20ee dm: move include ... |
25 |
#include <linux/dm-kcopyd.h> |
1da177e4c Linux-2.6.12-rc2 |
26 |
|
22a1ceb1e dm io: clean inte... |
27 |
#include "dm.h" |
1da177e4c Linux-2.6.12-rc2 |
28 |
|
c6ea41fbb dm kcopyd: preall... |
29 30 31 |
#define SUB_JOB_SIZE 128 #define SPLIT_COUNT 8 #define MIN_JOBS 8 |
5f43ba295 dm kcopyd: reserv... |
32 |
#define RESERVE_PAGES (DIV_ROUND_UP(SUB_JOB_SIZE << SECTOR_SHIFT, PAGE_SIZE)) |
c6ea41fbb dm kcopyd: preall... |
33 |
|
1da177e4c Linux-2.6.12-rc2 |
34 35 36 37 |
/*----------------------------------------------------------------- * Each kcopyd client has its own little pool of preallocated * pages for kcopyd io. *---------------------------------------------------------------*/ |
eb69aca5d dm kcopyd: clean ... |
38 |
struct dm_kcopyd_client { |
1da177e4c Linux-2.6.12-rc2 |
39 |
struct page_list *pages; |
d04714580 dm kcopyd: alloc ... |
40 41 |
unsigned nr_reserved_pages; unsigned nr_free_pages; |
138728dc9 [PATCH] dm snapsh... |
42 |
|
373a392bd dm kcopyd: update... |
43 |
struct dm_io_client *io_client; |
138728dc9 [PATCH] dm snapsh... |
44 45 |
wait_queue_head_t destroyq; atomic_t nr_jobs; |
8c0cbc2f7 dm kcopyd: per de... |
46 |
|
08d8757a4 dm kcopyd: privat... |
47 |
mempool_t *job_pool; |
8c0cbc2f7 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 Linux-2.6.12-rc2 |
64 |
}; |
7f0696539 dm kcopyd: add dm... |
65 |
static struct page_list zero_page_list; |
8c0cbc2f7 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 dm kcopyd: alloc ... |
70 71 72 |
/* * Obtain one page for the use of kcopyd. */ |
f99b55eec dm kcopyd: add gf... |
73 |
static struct page_list *alloc_pl(gfp_t gfp) |
1da177e4c Linux-2.6.12-rc2 |
74 75 |
{ struct page_list *pl; |
f99b55eec dm kcopyd: add gf... |
76 |
pl = kmalloc(sizeof(*pl), gfp); |
1da177e4c Linux-2.6.12-rc2 |
77 78 |
if (!pl) return NULL; |
f99b55eec dm kcopyd: add gf... |
79 |
pl->page = alloc_page(gfp); |
1da177e4c 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 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 Linux-2.6.12-rc2 |
98 |
{ |
d04714580 dm kcopyd: alloc ... |
99 |
struct page_list *next; |
1da177e4c Linux-2.6.12-rc2 |
100 |
|
d04714580 dm kcopyd: alloc ... |
101 102 |
do { next = pl->next; |
1da177e4c Linux-2.6.12-rc2 |
103 |
|
d04714580 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 Linux-2.6.12-rc2 |
111 |
|
d04714580 dm kcopyd: alloc ... |
112 113 |
pl = next; } while (pl); |
1da177e4c Linux-2.6.12-rc2 |
114 |
} |
d04714580 dm kcopyd: alloc ... |
115 116 |
static int kcopyd_get_pages(struct dm_kcopyd_client *kc, unsigned int nr, struct page_list **pages) |
1da177e4c Linux-2.6.12-rc2 |
117 |
{ |
d04714580 dm kcopyd: alloc ... |
118 |
struct page_list *pl; |
1da177e4c Linux-2.6.12-rc2 |
119 |
|
d04714580 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 Linux-2.6.12-rc2 |
137 |
|
d04714580 dm kcopyd: alloc ... |
138 139 140 141 |
out_of_memory: if (*pages) kcopyd_put_pages(kc, *pages); return -ENOMEM; |
1da177e4c 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 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 Linux-2.6.12-rc2 |
161 |
{ |
d04714580 dm kcopyd: alloc ... |
162 |
unsigned i; |
1da177e4c Linux-2.6.12-rc2 |
163 |
struct page_list *pl = NULL, *next; |
d04714580 dm kcopyd: alloc ... |
164 |
for (i = 0; i < nr_pages; i++) { |
f99b55eec dm kcopyd: add gf... |
165 |
next = alloc_pl(GFP_KERNEL); |
1da177e4c 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 dm kcopyd: alloc ... |
174 |
kc->nr_reserved_pages += nr_pages; |
1da177e4c Linux-2.6.12-rc2 |
175 |
kcopyd_put_pages(kc, pl); |
d04714580 dm kcopyd: alloc ... |
176 |
|
1da177e4c Linux-2.6.12-rc2 |
177 178 |
return 0; } |
eb69aca5d dm kcopyd: clean ... |
179 |
static void client_free_pages(struct dm_kcopyd_client *kc) |
1da177e4c Linux-2.6.12-rc2 |
180 |
{ |
d04714580 dm kcopyd: alloc ... |
181 |
BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages); |
1da177e4c Linux-2.6.12-rc2 |
182 183 |
drop_pages(kc->pages); kc->pages = NULL; |
d04714580 dm kcopyd: alloc ... |
184 |
kc->nr_free_pages = kc->nr_reserved_pages = 0; |
1da177e4c 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 dm kcopyd: clean ... |
193 |
struct dm_kcopyd_client *kc; |
1da177e4c 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 dm io: write erro... |
201 |
unsigned long write_err; |
1da177e4c Linux-2.6.12-rc2 |
202 203 204 205 206 |
/* * Either READ or WRITE */ int rw; |
22a1ceb1e dm io: clean inte... |
207 |
struct dm_io_region source; |
1da177e4c Linux-2.6.12-rc2 |
208 209 210 211 212 |
/* * The destinations for the transfer. */ unsigned int num_dests; |
eb69aca5d dm kcopyd: clean ... |
213 |
struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS]; |
1da177e4c Linux-2.6.12-rc2 |
214 |
|
1da177e4c 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 dm kcopyd: clean ... |
221 |
dm_kcopyd_notify_fn fn; |
1da177e4c 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 kcopyd use mutex ... |
228 |
struct mutex lock; |
1da177e4c Linux-2.6.12-rc2 |
229 230 |
atomic_t sub_jobs; sector_t progress; |
1da177e4c Linux-2.6.12-rc2 |
231 |
|
c6ea41fbb dm kcopyd: preall... |
232 233 |
struct kcopyd_job *master_job; }; |
1da177e4c Linux-2.6.12-rc2 |
234 |
|
e18b890bb [PATCH] slab: rem... |
235 |
static struct kmem_cache *_job_cache; |
1da177e4c Linux-2.6.12-rc2 |
236 |
|
945fa4d28 dm kcopyd: remove... |
237 |
int __init dm_kcopyd_init(void) |
1da177e4c Linux-2.6.12-rc2 |
238 |
{ |
c6ea41fbb 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 Linux-2.6.12-rc2 |
242 243 |
if (!_job_cache) return -ENOMEM; |
7f0696539 dm kcopyd: add dm... |
244 245 |
zero_page_list.next = &zero_page_list; zero_page_list.page = ZERO_PAGE(0); |
1da177e4c Linux-2.6.12-rc2 |
246 247 |
return 0; } |
945fa4d28 dm kcopyd: remove... |
248 |
void dm_kcopyd_exit(void) |
1da177e4c Linux-2.6.12-rc2 |
249 |
{ |
1da177e4c Linux-2.6.12-rc2 |
250 |
kmem_cache_destroy(_job_cache); |
1da177e4c 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 dm kcopyd: per de... |
258 259 |
static struct kcopyd_job *pop(struct list_head *jobs, struct dm_kcopyd_client *kc) |
1da177e4c Linux-2.6.12-rc2 |
260 261 262 |
{ struct kcopyd_job *job = NULL; unsigned long flags; |
8c0cbc2f7 dm kcopyd: per de... |
263 |
spin_lock_irqsave(&kc->job_lock, flags); |
1da177e4c 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 dm kcopyd: per de... |
269 |
spin_unlock_irqrestore(&kc->job_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
270 271 272 |
return job; } |
028867ac2 dm: use kmem_cach... |
273 |
static void push(struct list_head *jobs, struct kcopyd_job *job) |
1da177e4c Linux-2.6.12-rc2 |
274 275 |
{ unsigned long flags; |
8c0cbc2f7 dm kcopyd: per de... |
276 |
struct dm_kcopyd_client *kc = job->kc; |
1da177e4c Linux-2.6.12-rc2 |
277 |
|
8c0cbc2f7 dm kcopyd: per de... |
278 |
spin_lock_irqsave(&kc->job_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
279 |
list_add_tail(&job->list, jobs); |
8c0cbc2f7 dm kcopyd: per de... |
280 |
spin_unlock_irqrestore(&kc->job_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
281 |
} |
b673c3a81 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 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 dm io: write erro... |
305 |
unsigned long write_err = job->write_err; |
eb69aca5d dm kcopyd: clean ... |
306 307 |
dm_kcopyd_notify_fn fn = job->fn; struct dm_kcopyd_client *kc = job->kc; |
1da177e4c Linux-2.6.12-rc2 |
308 |
|
7f0696539 dm kcopyd: add dm... |
309 |
if (job->pages && job->pages != &zero_page_list) |
73830857b dm kcopyd: prepar... |
310 |
kcopyd_put_pages(kc, job->pages); |
c6ea41fbb 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 Linux-2.6.12-rc2 |
317 |
fn(read_err, write_err, context); |
138728dc9 [PATCH] dm snapsh... |
318 319 320 |
if (atomic_dec_and_test(&kc->nr_jobs)) wake_up(&kc->destroyq); |
1da177e4c 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 dm kcopyd: per de... |
327 |
struct dm_kcopyd_client *kc = job->kc; |
1da177e4c Linux-2.6.12-rc2 |
328 329 330 |
if (error) { if (job->rw == WRITE) |
ce503f59a [PATCH] dm kcopyd... |
331 |
job->write_err |= error; |
1da177e4c Linux-2.6.12-rc2 |
332 333 |
else job->read_err = 1; |
eb69aca5d dm kcopyd: clean ... |
334 |
if (!test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) { |
8c0cbc2f7 dm kcopyd: per de... |
335 336 |
push(&kc->complete_jobs, job); wake(kc); |
1da177e4c Linux-2.6.12-rc2 |
337 338 339 340 341 |
return; } } if (job->rw == WRITE) |
8c0cbc2f7 dm kcopyd: per de... |
342 |
push(&kc->complete_jobs, job); |
1da177e4c Linux-2.6.12-rc2 |
343 344 345 |
else { job->rw = WRITE; |
8c0cbc2f7 dm kcopyd: per de... |
346 |
push(&kc->io_jobs, job); |
1da177e4c Linux-2.6.12-rc2 |
347 |
} |
8c0cbc2f7 dm kcopyd: per de... |
348 |
wake(kc); |
1da177e4c 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 dm kcopyd: update... |
358 |
struct dm_io_request io_req = { |
8d35d3e37 dm kcopyd: delay ... |
359 |
.bi_rw = job->rw, |
373a392bd dm kcopyd: update... |
360 361 |
.mem.type = DM_IO_PAGE_LIST, .mem.ptr.pl = job->pages, |
4622afb3f dm kcopyd: remove... |
362 |
.mem.offset = 0, |
373a392bd dm kcopyd: update... |
363 364 365 366 |
.notify.fn = complete_io, .notify.context = job, .client = job->kc->io_client, }; |
1da177e4c Linux-2.6.12-rc2 |
367 |
|
7eaceacca block: remove per... |
368 |
if (job->rw == READ) |
373a392bd dm kcopyd: update... |
369 |
r = dm_io(&io_req, 1, &job->source, NULL); |
721a9602e block: kill off R... |
370 |
else |
373a392bd dm kcopyd: update... |
371 |
r = dm_io(&io_req, job->num_dests, job->dests, NULL); |
1da177e4c 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 dm kcopyd: remove... |
379 |
unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9); |
1da177e4c Linux-2.6.12-rc2 |
380 |
|
5bf45a3dc dm kcopyd: remove... |
381 |
r = kcopyd_get_pages(job->kc, nr_pages, &job->pages); |
1da177e4c Linux-2.6.12-rc2 |
382 383 |
if (!r) { /* this job is ready for io */ |
8c0cbc2f7 dm kcopyd: per de... |
384 |
push(&job->kc->io_jobs, job); |
1da177e4c 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 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 Linux-2.6.12-rc2 |
401 402 403 |
{ struct kcopyd_job *job; int r, count = 0; |
8c0cbc2f7 dm kcopyd: per de... |
404 |
while ((job = pop(jobs, kc))) { |
1da177e4c 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 dm io: write erro... |
411 |
job->write_err = (unsigned long) -1L; |
1da177e4c Linux-2.6.12-rc2 |
412 413 |
else job->read_err = 1; |
8c0cbc2f7 dm kcopyd: per de... |
414 |
push(&kc->complete_jobs, job); |
1da177e4c 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 dm kcopyd: avoid ... |
423 |
push_head(jobs, job); |
1da177e4c 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 dm kcopyd: per de... |
436 |
static void do_work(struct work_struct *work) |
1da177e4c Linux-2.6.12-rc2 |
437 |
{ |
8c0cbc2f7 dm kcopyd: per de... |
438 439 |
struct dm_kcopyd_client *kc = container_of(work, struct dm_kcopyd_client, kcopyd_work); |
7eaceacca block: remove per... |
440 |
struct blk_plug plug; |
8c0cbc2f7 dm kcopyd: per de... |
441 |
|
1da177e4c 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 block: remove per... |
449 |
blk_start_plug(&plug); |
8c0cbc2f7 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 block: remove per... |
453 |
blk_finish_plug(&plug); |
1da177e4c 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 dm kcopyd: per de... |
463 464 |
struct dm_kcopyd_client *kc = job->kc; atomic_inc(&kc->nr_jobs); |
9ca170a3c dm kcopyd: accept... |
465 466 |
if (unlikely(!job->source.count)) push(&kc->complete_jobs, job); |
7f0696539 dm kcopyd: add dm... |
467 468 |
else if (job->pages == &zero_page_list) push(&kc->io_jobs, job); |
9ca170a3c dm kcopyd: accept... |
469 470 |
else push(&kc->pages_jobs, job); |
8c0cbc2f7 dm kcopyd: per de... |
471 |
wake(kc); |
1da177e4c Linux-2.6.12-rc2 |
472 |
} |
4cdc1d1fa dm io: write erro... |
473 474 |
static void segment_complete(int read_err, unsigned long write_err, void *context) |
1da177e4c Linux-2.6.12-rc2 |
475 476 477 478 |
{ /* FIXME: tidy this function */ sector_t progress = 0; sector_t count = 0; |
c6ea41fbb dm kcopyd: preall... |
479 480 |
struct kcopyd_job *sub_job = (struct kcopyd_job *) context; struct kcopyd_job *job = sub_job->master_job; |
73830857b dm kcopyd: prepar... |
481 |
struct dm_kcopyd_client *kc = job->kc; |
1da177e4c Linux-2.6.12-rc2 |
482 |
|
def5b5b26 kcopyd use mutex ... |
483 |
mutex_lock(&job->lock); |
1da177e4c 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 [PATCH] dm kcopyd... |
490 |
job->write_err |= write_err; |
1da177e4c 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 dm kcopyd: clean ... |
496 |
test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) { |
1da177e4c 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 kcopyd use mutex ... |
507 |
mutex_unlock(&job->lock); |
1da177e4c Linux-2.6.12-rc2 |
508 509 510 |
if (count) { int i; |
1da177e4c 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 dm kcopyd: preall... |
522 |
sub_job->context = sub_job; |
1da177e4c Linux-2.6.12-rc2 |
523 524 525 526 527 |
dispatch_job(sub_job); } else if (atomic_dec_and_test(&job->sub_jobs)) { /* |
340cd4445 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 Linux-2.6.12-rc2 |
535 |
*/ |
340cd4445 dm kcopyd: fix ca... |
536 537 |
push(&kc->complete_jobs, job); wake(kc); |
1da177e4c Linux-2.6.12-rc2 |
538 539 540 541 |
} } /* |
c6ea41fbb dm kcopyd: preall... |
542 |
* Create some sub jobs to share the work between them. |
1da177e4c Linux-2.6.12-rc2 |
543 |
*/ |
c6ea41fbb dm kcopyd: preall... |
544 |
static void split_job(struct kcopyd_job *master_job) |
1da177e4c Linux-2.6.12-rc2 |
545 546 |
{ int i; |
c6ea41fbb dm kcopyd: preall... |
547 |
atomic_inc(&master_job->kc->nr_jobs); |
340cd4445 dm kcopyd: fix ca... |
548 |
|
c6ea41fbb 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 Linux-2.6.12-rc2 |
554 |
} |
eb69aca5d 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 Linux-2.6.12-rc2 |
558 559 560 561 |
{ struct kcopyd_job *job; /* |
c6ea41fbb dm kcopyd: preall... |
562 563 |
* Allocate an array of jobs consisting of one master job * followed by SPLIT_COUNT sub jobs. |
1da177e4c Linux-2.6.12-rc2 |
564 |
*/ |
08d8757a4 dm kcopyd: privat... |
565 |
job = mempool_alloc(kc->job_pool, GFP_NOIO); |
1da177e4c 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 Linux-2.6.12-rc2 |
574 575 576 |
job->num_dests = num_dests; memcpy(&job->dests, dests, sizeof(*dests) * num_dests); |
7f0696539 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 Linux-2.6.12-rc2 |
587 588 589 |
job->fn = fn; job->context = context; |
c6ea41fbb dm kcopyd: preall... |
590 |
job->master_job = job; |
1da177e4c Linux-2.6.12-rc2 |
591 |
|
a705a34a5 dm kcopyd: avoid ... |
592 |
if (job->source.count <= SUB_JOB_SIZE) |
1da177e4c Linux-2.6.12-rc2 |
593 |
dispatch_job(job); |
1da177e4c Linux-2.6.12-rc2 |
594 |
else { |
def5b5b26 kcopyd use mutex ... |
595 |
mutex_init(&job->lock); |
1da177e4c Linux-2.6.12-rc2 |
596 597 598 599 600 601 |
job->progress = 0; split_job(job); } return 0; } |
eb69aca5d dm kcopyd: clean ... |
602 |
EXPORT_SYMBOL(dm_kcopyd_copy); |
1da177e4c Linux-2.6.12-rc2 |
603 |
|
7f0696539 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 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 dm kcopyd: fix jo... |
622 |
job->master_job = job; |
a6e50b409 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 Linux-2.6.12-rc2 |
642 643 644 645 |
/* * Cancels a kcopyd job, eg. someone might be deactivating a * mirror. */ |
0b56306e5 [PATCH] drivers/m... |
646 |
#if 0 |
1da177e4c Linux-2.6.12-rc2 |
647 648 649 650 651 |
int kcopyd_cancel(struct kcopyd_job *job, int block) { /* FIXME: finish */ return -1; } |
0b56306e5 [PATCH] drivers/m... |
652 |
#endif /* 0 */ |
1da177e4c Linux-2.6.12-rc2 |
653 654 |
/*----------------------------------------------------------------- |
945fa4d28 dm kcopyd: remove... |
655 |
* Client setup |
1da177e4c Linux-2.6.12-rc2 |
656 |
*---------------------------------------------------------------*/ |
fa34ce730 dm kcopyd: return... |
657 |
struct dm_kcopyd_client *dm_kcopyd_client_create(void) |
1da177e4c Linux-2.6.12-rc2 |
658 |
{ |
945fa4d28 dm kcopyd: remove... |
659 |
int r = -ENOMEM; |
eb69aca5d dm kcopyd: clean ... |
660 |
struct dm_kcopyd_client *kc; |
1da177e4c Linux-2.6.12-rc2 |
661 |
|
1da177e4c Linux-2.6.12-rc2 |
662 |
kc = kmalloc(sizeof(*kc), GFP_KERNEL); |
945fa4d28 dm kcopyd: remove... |
663 |
if (!kc) |
fa34ce730 dm kcopyd: return... |
664 |
return ERR_PTR(-ENOMEM); |
1da177e4c Linux-2.6.12-rc2 |
665 |
|
8c0cbc2f7 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 dm kcopyd: privat... |
670 |
kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); |
945fa4d28 dm kcopyd: remove... |
671 672 |
if (!kc->job_pool) goto bad_slab; |
08d8757a4 dm kcopyd: privat... |
673 |
|
8c0cbc2f7 dm kcopyd: per de... |
674 |
INIT_WORK(&kc->kcopyd_work, do_work); |
9c4376de9 dm: use non reent... |
675 676 |
kc->kcopyd_wq = alloc_workqueue("kcopyd", WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); |
945fa4d28 dm kcopyd: remove... |
677 678 |
if (!kc->kcopyd_wq) goto bad_workqueue; |
8c0cbc2f7 dm kcopyd: per de... |
679 |
|
1da177e4c Linux-2.6.12-rc2 |
680 |
kc->pages = NULL; |
d04714580 dm kcopyd: alloc ... |
681 |
kc->nr_reserved_pages = kc->nr_free_pages = 0; |
5f43ba295 dm kcopyd: reserv... |
682 |
r = client_reserve_pages(kc, RESERVE_PAGES); |
945fa4d28 dm kcopyd: remove... |
683 684 |
if (r) goto bad_client_pages; |
1da177e4c Linux-2.6.12-rc2 |
685 |
|
bda8efec5 dm io: use fixed ... |
686 |
kc->io_client = dm_io_client_create(); |
373a392bd dm kcopyd: update... |
687 688 |
if (IS_ERR(kc->io_client)) { r = PTR_ERR(kc->io_client); |
945fa4d28 dm kcopyd: remove... |
689 |
goto bad_io_client; |
1da177e4c Linux-2.6.12-rc2 |
690 |
} |
138728dc9 [PATCH] dm snapsh... |
691 692 |
init_waitqueue_head(&kc->destroyq); atomic_set(&kc->nr_jobs, 0); |
fa34ce730 dm kcopyd: return... |
693 |
return kc; |
945fa4d28 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 dm kcopyd: return... |
703 |
return ERR_PTR(r); |
1da177e4c Linux-2.6.12-rc2 |
704 |
} |
eb69aca5d dm kcopyd: clean ... |
705 |
EXPORT_SYMBOL(dm_kcopyd_client_create); |
1da177e4c Linux-2.6.12-rc2 |
706 |
|
eb69aca5d dm kcopyd: clean ... |
707 |
void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc) |
1da177e4c Linux-2.6.12-rc2 |
708 |
{ |
138728dc9 [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 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 dm kcopyd: update... |
715 |
dm_io_client_destroy(kc->io_client); |
1da177e4c Linux-2.6.12-rc2 |
716 |
client_free_pages(kc); |
08d8757a4 dm kcopyd: privat... |
717 |
mempool_destroy(kc->job_pool); |
1da177e4c Linux-2.6.12-rc2 |
718 |
kfree(kc); |
1da177e4c Linux-2.6.12-rc2 |
719 |
} |
eb69aca5d dm kcopyd: clean ... |
720 |
EXPORT_SYMBOL(dm_kcopyd_client_destroy); |