diff options
Diffstat (limited to 'drivers/md/dm-kcopyd.c')
-rw-r--r-- | drivers/md/dm-kcopyd.c | 176 |
1 files changed, 104 insertions, 72 deletions
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index d8587bac5682..819e37eaaeba 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c | |||
@@ -27,15 +27,19 @@ | |||
27 | 27 | ||
28 | #include "dm.h" | 28 | #include "dm.h" |
29 | 29 | ||
30 | #define SUB_JOB_SIZE 128 | ||
31 | #define SPLIT_COUNT 8 | ||
32 | #define MIN_JOBS 8 | ||
33 | #define RESERVE_PAGES (DIV_ROUND_UP(SUB_JOB_SIZE << SECTOR_SHIFT, PAGE_SIZE)) | ||
34 | |||
30 | /*----------------------------------------------------------------- | 35 | /*----------------------------------------------------------------- |
31 | * Each kcopyd client has its own little pool of preallocated | 36 | * Each kcopyd client has its own little pool of preallocated |
32 | * pages for kcopyd io. | 37 | * pages for kcopyd io. |
33 | *---------------------------------------------------------------*/ | 38 | *---------------------------------------------------------------*/ |
34 | struct dm_kcopyd_client { | 39 | struct dm_kcopyd_client { |
35 | spinlock_t lock; | ||
36 | struct page_list *pages; | 40 | struct page_list *pages; |
37 | unsigned int nr_pages; | 41 | unsigned nr_reserved_pages; |
38 | unsigned int nr_free_pages; | 42 | unsigned nr_free_pages; |
39 | 43 | ||
40 | struct dm_io_client *io_client; | 44 | struct dm_io_client *io_client; |
41 | 45 | ||
@@ -67,15 +71,18 @@ static void wake(struct dm_kcopyd_client *kc) | |||
67 | queue_work(kc->kcopyd_wq, &kc->kcopyd_work); | 71 | queue_work(kc->kcopyd_wq, &kc->kcopyd_work); |
68 | } | 72 | } |
69 | 73 | ||
70 | static struct page_list *alloc_pl(void) | 74 | /* |
75 | * Obtain one page for the use of kcopyd. | ||
76 | */ | ||
77 | static struct page_list *alloc_pl(gfp_t gfp) | ||
71 | { | 78 | { |
72 | struct page_list *pl; | 79 | struct page_list *pl; |
73 | 80 | ||
74 | pl = kmalloc(sizeof(*pl), GFP_KERNEL); | 81 | pl = kmalloc(sizeof(*pl), gfp); |
75 | if (!pl) | 82 | if (!pl) |
76 | return NULL; | 83 | return NULL; |
77 | 84 | ||
78 | pl->page = alloc_page(GFP_KERNEL); | 85 | pl->page = alloc_page(gfp); |
79 | if (!pl->page) { | 86 | if (!pl->page) { |
80 | kfree(pl); | 87 | kfree(pl); |
81 | return NULL; | 88 | return NULL; |
@@ -90,41 +97,56 @@ static void free_pl(struct page_list *pl) | |||
90 | kfree(pl); | 97 | kfree(pl); |
91 | } | 98 | } |
92 | 99 | ||
93 | static int kcopyd_get_pages(struct dm_kcopyd_client *kc, | 100 | /* |
94 | unsigned int nr, struct page_list **pages) | 101 | * Add the provided pages to a client's free page list, releasing |
102 | * back to the system any beyond the reserved_pages limit. | ||
103 | */ | ||
104 | static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl) | ||
95 | { | 105 | { |
96 | struct page_list *pl; | 106 | struct page_list *next; |
97 | |||
98 | spin_lock(&kc->lock); | ||
99 | if (kc->nr_free_pages < nr) { | ||
100 | spin_unlock(&kc->lock); | ||
101 | return -ENOMEM; | ||
102 | } | ||
103 | |||
104 | kc->nr_free_pages -= nr; | ||
105 | for (*pages = pl = kc->pages; --nr; pl = pl->next) | ||
106 | ; | ||
107 | 107 | ||
108 | kc->pages = pl->next; | 108 | do { |
109 | pl->next = NULL; | 109 | next = pl->next; |
110 | 110 | ||
111 | spin_unlock(&kc->lock); | 111 | if (kc->nr_free_pages >= kc->nr_reserved_pages) |
112 | free_pl(pl); | ||
113 | else { | ||
114 | pl->next = kc->pages; | ||
115 | kc->pages = pl; | ||
116 | kc->nr_free_pages++; | ||
117 | } | ||
112 | 118 | ||
113 | return 0; | 119 | pl = next; |
120 | } while (pl); | ||
114 | } | 121 | } |
115 | 122 | ||
116 | static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl) | 123 | static int kcopyd_get_pages(struct dm_kcopyd_client *kc, |
124 | unsigned int nr, struct page_list **pages) | ||
117 | { | 125 | { |
118 | struct page_list *cursor; | 126 | struct page_list *pl; |
127 | |||
128 | *pages = NULL; | ||
129 | |||
130 | do { | ||
131 | pl = alloc_pl(__GFP_NOWARN | __GFP_NORETRY); | ||
132 | if (unlikely(!pl)) { | ||
133 | /* Use reserved pages */ | ||
134 | pl = kc->pages; | ||
135 | if (unlikely(!pl)) | ||
136 | goto out_of_memory; | ||
137 | kc->pages = pl->next; | ||
138 | kc->nr_free_pages--; | ||
139 | } | ||
140 | pl->next = *pages; | ||
141 | *pages = pl; | ||
142 | } while (--nr); | ||
119 | 143 | ||
120 | spin_lock(&kc->lock); | 144 | return 0; |
121 | for (cursor = pl; cursor->next; cursor = cursor->next) | ||
122 | kc->nr_free_pages++; | ||
123 | 145 | ||
124 | kc->nr_free_pages++; | 146 | out_of_memory: |
125 | cursor->next = kc->pages; | 147 | if (*pages) |
126 | kc->pages = pl; | 148 | kcopyd_put_pages(kc, *pages); |
127 | spin_unlock(&kc->lock); | 149 | return -ENOMEM; |
128 | } | 150 | } |
129 | 151 | ||
130 | /* | 152 | /* |
@@ -141,13 +163,16 @@ static void drop_pages(struct page_list *pl) | |||
141 | } | 163 | } |
142 | } | 164 | } |
143 | 165 | ||
144 | static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr) | 166 | /* |
167 | * Allocate and reserve nr_pages for the use of a specific client. | ||
168 | */ | ||
169 | static int client_reserve_pages(struct dm_kcopyd_client *kc, unsigned nr_pages) | ||
145 | { | 170 | { |
146 | unsigned int i; | 171 | unsigned i; |
147 | struct page_list *pl = NULL, *next; | 172 | struct page_list *pl = NULL, *next; |
148 | 173 | ||
149 | for (i = 0; i < nr; i++) { | 174 | for (i = 0; i < nr_pages; i++) { |
150 | next = alloc_pl(); | 175 | next = alloc_pl(GFP_KERNEL); |
151 | if (!next) { | 176 | if (!next) { |
152 | if (pl) | 177 | if (pl) |
153 | drop_pages(pl); | 178 | drop_pages(pl); |
@@ -157,17 +182,18 @@ static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr) | |||
157 | pl = next; | 182 | pl = next; |
158 | } | 183 | } |
159 | 184 | ||
185 | kc->nr_reserved_pages += nr_pages; | ||
160 | kcopyd_put_pages(kc, pl); | 186 | kcopyd_put_pages(kc, pl); |
161 | kc->nr_pages += nr; | 187 | |
162 | return 0; | 188 | return 0; |
163 | } | 189 | } |
164 | 190 | ||
165 | static void client_free_pages(struct dm_kcopyd_client *kc) | 191 | static void client_free_pages(struct dm_kcopyd_client *kc) |
166 | { | 192 | { |
167 | BUG_ON(kc->nr_free_pages != kc->nr_pages); | 193 | BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages); |
168 | drop_pages(kc->pages); | 194 | drop_pages(kc->pages); |
169 | kc->pages = NULL; | 195 | kc->pages = NULL; |
170 | kc->nr_free_pages = kc->nr_pages = 0; | 196 | kc->nr_free_pages = kc->nr_reserved_pages = 0; |
171 | } | 197 | } |
172 | 198 | ||
173 | /*----------------------------------------------------------------- | 199 | /*----------------------------------------------------------------- |
@@ -216,16 +242,17 @@ struct kcopyd_job { | |||
216 | struct mutex lock; | 242 | struct mutex lock; |
217 | atomic_t sub_jobs; | 243 | atomic_t sub_jobs; |
218 | sector_t progress; | 244 | sector_t progress; |
219 | }; | ||
220 | 245 | ||
221 | /* FIXME: this should scale with the number of pages */ | 246 | struct kcopyd_job *master_job; |
222 | #define MIN_JOBS 512 | 247 | }; |
223 | 248 | ||
224 | static struct kmem_cache *_job_cache; | 249 | static struct kmem_cache *_job_cache; |
225 | 250 | ||
226 | int __init dm_kcopyd_init(void) | 251 | int __init dm_kcopyd_init(void) |
227 | { | 252 | { |
228 | _job_cache = KMEM_CACHE(kcopyd_job, 0); | 253 | _job_cache = kmem_cache_create("kcopyd_job", |
254 | sizeof(struct kcopyd_job) * (SPLIT_COUNT + 1), | ||
255 | __alignof__(struct kcopyd_job), 0, NULL); | ||
229 | if (!_job_cache) | 256 | if (!_job_cache) |
230 | return -ENOMEM; | 257 | return -ENOMEM; |
231 | 258 | ||
@@ -299,7 +326,12 @@ static int run_complete_job(struct kcopyd_job *job) | |||
299 | 326 | ||
300 | if (job->pages) | 327 | if (job->pages) |
301 | kcopyd_put_pages(kc, job->pages); | 328 | kcopyd_put_pages(kc, job->pages); |
302 | mempool_free(job, kc->job_pool); | 329 | /* |
330 | * If this is the master job, the sub jobs have already | ||
331 | * completed so we can free everything. | ||
332 | */ | ||
333 | if (job->master_job == job) | ||
334 | mempool_free(job, kc->job_pool); | ||
303 | fn(read_err, write_err, context); | 335 | fn(read_err, write_err, context); |
304 | 336 | ||
305 | if (atomic_dec_and_test(&kc->nr_jobs)) | 337 | if (atomic_dec_and_test(&kc->nr_jobs)) |
@@ -345,7 +377,7 @@ static int run_io_job(struct kcopyd_job *job) | |||
345 | { | 377 | { |
346 | int r; | 378 | int r; |
347 | struct dm_io_request io_req = { | 379 | struct dm_io_request io_req = { |
348 | .bi_rw = job->rw | REQ_SYNC | REQ_UNPLUG, | 380 | .bi_rw = job->rw, |
349 | .mem.type = DM_IO_PAGE_LIST, | 381 | .mem.type = DM_IO_PAGE_LIST, |
350 | .mem.ptr.pl = job->pages, | 382 | .mem.ptr.pl = job->pages, |
351 | .mem.offset = job->offset, | 383 | .mem.offset = job->offset, |
@@ -428,6 +460,7 @@ static void do_work(struct work_struct *work) | |||
428 | { | 460 | { |
429 | struct dm_kcopyd_client *kc = container_of(work, | 461 | struct dm_kcopyd_client *kc = container_of(work, |
430 | struct dm_kcopyd_client, kcopyd_work); | 462 | struct dm_kcopyd_client, kcopyd_work); |
463 | struct blk_plug plug; | ||
431 | 464 | ||
432 | /* | 465 | /* |
433 | * The order that these are called is *very* important. | 466 | * The order that these are called is *very* important. |
@@ -436,9 +469,11 @@ static void do_work(struct work_struct *work) | |||
436 | * list. io jobs call wake when they complete and it all | 469 | * list. io jobs call wake when they complete and it all |
437 | * starts again. | 470 | * starts again. |
438 | */ | 471 | */ |
472 | blk_start_plug(&plug); | ||
439 | process_jobs(&kc->complete_jobs, kc, run_complete_job); | 473 | process_jobs(&kc->complete_jobs, kc, run_complete_job); |
440 | process_jobs(&kc->pages_jobs, kc, run_pages_job); | 474 | process_jobs(&kc->pages_jobs, kc, run_pages_job); |
441 | process_jobs(&kc->io_jobs, kc, run_io_job); | 475 | process_jobs(&kc->io_jobs, kc, run_io_job); |
476 | blk_finish_plug(&plug); | ||
442 | } | 477 | } |
443 | 478 | ||
444 | /* | 479 | /* |
@@ -457,14 +492,14 @@ static void dispatch_job(struct kcopyd_job *job) | |||
457 | wake(kc); | 492 | wake(kc); |
458 | } | 493 | } |
459 | 494 | ||
460 | #define SUB_JOB_SIZE 128 | ||
461 | static void segment_complete(int read_err, unsigned long write_err, | 495 | static void segment_complete(int read_err, unsigned long write_err, |
462 | void *context) | 496 | void *context) |
463 | { | 497 | { |
464 | /* FIXME: tidy this function */ | 498 | /* FIXME: tidy this function */ |
465 | sector_t progress = 0; | 499 | sector_t progress = 0; |
466 | sector_t count = 0; | 500 | sector_t count = 0; |
467 | struct kcopyd_job *job = (struct kcopyd_job *) context; | 501 | struct kcopyd_job *sub_job = (struct kcopyd_job *) context; |
502 | struct kcopyd_job *job = sub_job->master_job; | ||
468 | struct dm_kcopyd_client *kc = job->kc; | 503 | struct dm_kcopyd_client *kc = job->kc; |
469 | 504 | ||
470 | mutex_lock(&job->lock); | 505 | mutex_lock(&job->lock); |
@@ -495,8 +530,6 @@ static void segment_complete(int read_err, unsigned long write_err, | |||
495 | 530 | ||
496 | if (count) { | 531 | if (count) { |
497 | int i; | 532 | int i; |
498 | struct kcopyd_job *sub_job = mempool_alloc(kc->job_pool, | ||
499 | GFP_NOIO); | ||
500 | 533 | ||
501 | *sub_job = *job; | 534 | *sub_job = *job; |
502 | sub_job->source.sector += progress; | 535 | sub_job->source.sector += progress; |
@@ -508,7 +541,7 @@ static void segment_complete(int read_err, unsigned long write_err, | |||
508 | } | 541 | } |
509 | 542 | ||
510 | sub_job->fn = segment_complete; | 543 | sub_job->fn = segment_complete; |
511 | sub_job->context = job; | 544 | sub_job->context = sub_job; |
512 | dispatch_job(sub_job); | 545 | dispatch_job(sub_job); |
513 | 546 | ||
514 | } else if (atomic_dec_and_test(&job->sub_jobs)) { | 547 | } else if (atomic_dec_and_test(&job->sub_jobs)) { |
@@ -528,19 +561,19 @@ static void segment_complete(int read_err, unsigned long write_err, | |||
528 | } | 561 | } |
529 | 562 | ||
530 | /* | 563 | /* |
531 | * Create some little jobs that will do the move between | 564 | * Create some sub jobs to share the work between them. |
532 | * them. | ||
533 | */ | 565 | */ |
534 | #define SPLIT_COUNT 8 | 566 | static void split_job(struct kcopyd_job *master_job) |
535 | static void split_job(struct kcopyd_job *job) | ||
536 | { | 567 | { |
537 | int i; | 568 | int i; |
538 | 569 | ||
539 | atomic_inc(&job->kc->nr_jobs); | 570 | atomic_inc(&master_job->kc->nr_jobs); |
540 | 571 | ||
541 | atomic_set(&job->sub_jobs, SPLIT_COUNT); | 572 | atomic_set(&master_job->sub_jobs, SPLIT_COUNT); |
542 | for (i = 0; i < SPLIT_COUNT; i++) | 573 | for (i = 0; i < SPLIT_COUNT; i++) { |
543 | segment_complete(0, 0u, job); | 574 | master_job[i + 1].master_job = master_job; |
575 | segment_complete(0, 0u, &master_job[i + 1]); | ||
576 | } | ||
544 | } | 577 | } |
545 | 578 | ||
546 | int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, | 579 | int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, |
@@ -550,7 +583,8 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, | |||
550 | struct kcopyd_job *job; | 583 | struct kcopyd_job *job; |
551 | 584 | ||
552 | /* | 585 | /* |
553 | * Allocate a new job. | 586 | * Allocate an array of jobs consisting of one master job |
587 | * followed by SPLIT_COUNT sub jobs. | ||
554 | */ | 588 | */ |
555 | job = mempool_alloc(kc->job_pool, GFP_NOIO); | 589 | job = mempool_alloc(kc->job_pool, GFP_NOIO); |
556 | 590 | ||
@@ -574,10 +608,10 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, | |||
574 | 608 | ||
575 | job->fn = fn; | 609 | job->fn = fn; |
576 | job->context = context; | 610 | job->context = context; |
611 | job->master_job = job; | ||
577 | 612 | ||
578 | if (job->source.count < SUB_JOB_SIZE) | 613 | if (job->source.count <= SUB_JOB_SIZE) |
579 | dispatch_job(job); | 614 | dispatch_job(job); |
580 | |||
581 | else { | 615 | else { |
582 | mutex_init(&job->lock); | 616 | mutex_init(&job->lock); |
583 | job->progress = 0; | 617 | job->progress = 0; |
@@ -603,17 +637,15 @@ int kcopyd_cancel(struct kcopyd_job *job, int block) | |||
603 | /*----------------------------------------------------------------- | 637 | /*----------------------------------------------------------------- |
604 | * Client setup | 638 | * Client setup |
605 | *---------------------------------------------------------------*/ | 639 | *---------------------------------------------------------------*/ |
606 | int dm_kcopyd_client_create(unsigned int nr_pages, | 640 | struct dm_kcopyd_client *dm_kcopyd_client_create(void) |
607 | struct dm_kcopyd_client **result) | ||
608 | { | 641 | { |
609 | int r = -ENOMEM; | 642 | int r = -ENOMEM; |
610 | struct dm_kcopyd_client *kc; | 643 | struct dm_kcopyd_client *kc; |
611 | 644 | ||
612 | kc = kmalloc(sizeof(*kc), GFP_KERNEL); | 645 | kc = kmalloc(sizeof(*kc), GFP_KERNEL); |
613 | if (!kc) | 646 | if (!kc) |
614 | return -ENOMEM; | 647 | return ERR_PTR(-ENOMEM); |
615 | 648 | ||
616 | spin_lock_init(&kc->lock); | ||
617 | spin_lock_init(&kc->job_lock); | 649 | spin_lock_init(&kc->job_lock); |
618 | INIT_LIST_HEAD(&kc->complete_jobs); | 650 | INIT_LIST_HEAD(&kc->complete_jobs); |
619 | INIT_LIST_HEAD(&kc->io_jobs); | 651 | INIT_LIST_HEAD(&kc->io_jobs); |
@@ -624,17 +656,18 @@ int dm_kcopyd_client_create(unsigned int nr_pages, | |||
624 | goto bad_slab; | 656 | goto bad_slab; |
625 | 657 | ||
626 | INIT_WORK(&kc->kcopyd_work, do_work); | 658 | INIT_WORK(&kc->kcopyd_work, do_work); |
627 | kc->kcopyd_wq = create_singlethread_workqueue("kcopyd"); | 659 | kc->kcopyd_wq = alloc_workqueue("kcopyd", |
660 | WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); | ||
628 | if (!kc->kcopyd_wq) | 661 | if (!kc->kcopyd_wq) |
629 | goto bad_workqueue; | 662 | goto bad_workqueue; |
630 | 663 | ||
631 | kc->pages = NULL; | 664 | kc->pages = NULL; |
632 | kc->nr_pages = kc->nr_free_pages = 0; | 665 | kc->nr_reserved_pages = kc->nr_free_pages = 0; |
633 | r = client_alloc_pages(kc, nr_pages); | 666 | r = client_reserve_pages(kc, RESERVE_PAGES); |
634 | if (r) | 667 | if (r) |
635 | goto bad_client_pages; | 668 | goto bad_client_pages; |
636 | 669 | ||
637 | kc->io_client = dm_io_client_create(nr_pages); | 670 | kc->io_client = dm_io_client_create(); |
638 | if (IS_ERR(kc->io_client)) { | 671 | if (IS_ERR(kc->io_client)) { |
639 | r = PTR_ERR(kc->io_client); | 672 | r = PTR_ERR(kc->io_client); |
640 | goto bad_io_client; | 673 | goto bad_io_client; |
@@ -643,8 +676,7 @@ int dm_kcopyd_client_create(unsigned int nr_pages, | |||
643 | init_waitqueue_head(&kc->destroyq); | 676 | init_waitqueue_head(&kc->destroyq); |
644 | atomic_set(&kc->nr_jobs, 0); | 677 | atomic_set(&kc->nr_jobs, 0); |
645 | 678 | ||
646 | *result = kc; | 679 | return kc; |
647 | return 0; | ||
648 | 680 | ||
649 | bad_io_client: | 681 | bad_io_client: |
650 | client_free_pages(kc); | 682 | client_free_pages(kc); |
@@ -655,7 +687,7 @@ bad_workqueue: | |||
655 | bad_slab: | 687 | bad_slab: |
656 | kfree(kc); | 688 | kfree(kc); |
657 | 689 | ||
658 | return r; | 690 | return ERR_PTR(r); |
659 | } | 691 | } |
660 | EXPORT_SYMBOL(dm_kcopyd_client_create); | 692 | EXPORT_SYMBOL(dm_kcopyd_client_create); |
661 | 693 | ||