diff options
| -rw-r--r-- | drivers/md/dm-kcopyd.c | 91 |
1 files changed, 60 insertions, 31 deletions
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 0270844c2a3d..5dfbdcb40a47 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c | |||
| @@ -37,8 +37,8 @@ | |||
| 37 | *---------------------------------------------------------------*/ | 37 | *---------------------------------------------------------------*/ |
| 38 | struct dm_kcopyd_client { | 38 | struct dm_kcopyd_client { |
| 39 | struct page_list *pages; | 39 | struct page_list *pages; |
| 40 | unsigned int nr_pages; | 40 | unsigned nr_reserved_pages; |
| 41 | unsigned int nr_free_pages; | 41 | unsigned nr_free_pages; |
| 42 | 42 | ||
| 43 | struct dm_io_client *io_client; | 43 | struct dm_io_client *io_client; |
| 44 | 44 | ||
| @@ -70,6 +70,9 @@ static void wake(struct dm_kcopyd_client *kc) | |||
| 70 | queue_work(kc->kcopyd_wq, &kc->kcopyd_work); | 70 | queue_work(kc->kcopyd_wq, &kc->kcopyd_work); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | /* | ||
| 74 | * Obtain one page for the use of kcopyd. | ||
| 75 | */ | ||
| 73 | static struct page_list *alloc_pl(gfp_t gfp) | 76 | static struct page_list *alloc_pl(gfp_t gfp) |
| 74 | { | 77 | { |
| 75 | struct page_list *pl; | 78 | struct page_list *pl; |
| @@ -93,34 +96,56 @@ static void free_pl(struct page_list *pl) | |||
| 93 | kfree(pl); | 96 | kfree(pl); |
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | static int kcopyd_get_pages(struct dm_kcopyd_client *kc, | 99 | /* |
| 97 | unsigned int nr, struct page_list **pages) | 100 | * Add the provided pages to a client's free page list, releasing |
| 101 | * back to the system any beyond the reserved_pages limit. | ||
| 102 | */ | ||
| 103 | static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl) | ||
| 98 | { | 104 | { |
| 99 | struct page_list *pl; | 105 | struct page_list *next; |
| 100 | |||
| 101 | if (kc->nr_free_pages < nr) | ||
| 102 | return -ENOMEM; | ||
| 103 | 106 | ||
| 104 | kc->nr_free_pages -= nr; | 107 | do { |
| 105 | for (*pages = pl = kc->pages; --nr; pl = pl->next) | 108 | next = pl->next; |
| 106 | ; | ||
| 107 | 109 | ||
| 108 | kc->pages = pl->next; | 110 | if (kc->nr_free_pages >= kc->nr_reserved_pages) |
| 109 | pl->next = NULL; | 111 | free_pl(pl); |
| 112 | else { | ||
| 113 | pl->next = kc->pages; | ||
| 114 | kc->pages = pl; | ||
| 115 | kc->nr_free_pages++; | ||
| 116 | } | ||
| 110 | 117 | ||
| 111 | return 0; | 118 | pl = next; |
| 119 | } while (pl); | ||
| 112 | } | 120 | } |
| 113 | 121 | ||
| 114 | static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl) | 122 | static int kcopyd_get_pages(struct dm_kcopyd_client *kc, |
| 123 | unsigned int nr, struct page_list **pages) | ||
| 115 | { | 124 | { |
| 116 | struct page_list *cursor; | 125 | struct page_list *pl; |
| 117 | 126 | ||
| 118 | for (cursor = pl; cursor->next; cursor = cursor->next) | 127 | *pages = NULL; |
| 119 | kc->nr_free_pages++; | 128 | |
| 129 | do { | ||
| 130 | pl = alloc_pl(__GFP_NOWARN | __GFP_NORETRY); | ||
| 131 | if (unlikely(!pl)) { | ||
| 132 | /* Use reserved pages */ | ||
| 133 | pl = kc->pages; | ||
| 134 | if (unlikely(!pl)) | ||
| 135 | goto out_of_memory; | ||
| 136 | kc->pages = pl->next; | ||
| 137 | kc->nr_free_pages--; | ||
| 138 | } | ||
| 139 | pl->next = *pages; | ||
| 140 | *pages = pl; | ||
| 141 | } while (--nr); | ||
| 142 | |||
| 143 | return 0; | ||
| 120 | 144 | ||
| 121 | kc->nr_free_pages++; | 145 | out_of_memory: |
| 122 | cursor->next = kc->pages; | 146 | if (*pages) |
| 123 | kc->pages = pl; | 147 | kcopyd_put_pages(kc, *pages); |
| 148 | return -ENOMEM; | ||
| 124 | } | 149 | } |
| 125 | 150 | ||
| 126 | /* | 151 | /* |
| @@ -137,12 +162,15 @@ static void drop_pages(struct page_list *pl) | |||
| 137 | } | 162 | } |
| 138 | } | 163 | } |
| 139 | 164 | ||
| 140 | static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr) | 165 | /* |
| 166 | * Allocate and reserve nr_pages for the use of a specific client. | ||
| 167 | */ | ||
| 168 | static int client_reserve_pages(struct dm_kcopyd_client *kc, unsigned nr_pages) | ||
| 141 | { | 169 | { |
| 142 | unsigned int i; | 170 | unsigned i; |
| 143 | struct page_list *pl = NULL, *next; | 171 | struct page_list *pl = NULL, *next; |
| 144 | 172 | ||
| 145 | for (i = 0; i < nr; i++) { | 173 | for (i = 0; i < nr_pages; i++) { |
| 146 | next = alloc_pl(GFP_KERNEL); | 174 | next = alloc_pl(GFP_KERNEL); |
| 147 | if (!next) { | 175 | if (!next) { |
| 148 | if (pl) | 176 | if (pl) |
| @@ -153,17 +181,18 @@ static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr) | |||
| 153 | pl = next; | 181 | pl = next; |
| 154 | } | 182 | } |
| 155 | 183 | ||
| 184 | kc->nr_reserved_pages += nr_pages; | ||
| 156 | kcopyd_put_pages(kc, pl); | 185 | kcopyd_put_pages(kc, pl); |
| 157 | kc->nr_pages += nr; | 186 | |
| 158 | return 0; | 187 | return 0; |
| 159 | } | 188 | } |
| 160 | 189 | ||
| 161 | static void client_free_pages(struct dm_kcopyd_client *kc) | 190 | static void client_free_pages(struct dm_kcopyd_client *kc) |
| 162 | { | 191 | { |
| 163 | BUG_ON(kc->nr_free_pages != kc->nr_pages); | 192 | BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages); |
| 164 | drop_pages(kc->pages); | 193 | drop_pages(kc->pages); |
| 165 | kc->pages = NULL; | 194 | kc->pages = NULL; |
| 166 | kc->nr_free_pages = kc->nr_pages = 0; | 195 | kc->nr_free_pages = kc->nr_reserved_pages = 0; |
| 167 | } | 196 | } |
| 168 | 197 | ||
| 169 | /*----------------------------------------------------------------- | 198 | /*----------------------------------------------------------------- |
| @@ -607,7 +636,7 @@ int kcopyd_cancel(struct kcopyd_job *job, int block) | |||
| 607 | /*----------------------------------------------------------------- | 636 | /*----------------------------------------------------------------- |
| 608 | * Client setup | 637 | * Client setup |
| 609 | *---------------------------------------------------------------*/ | 638 | *---------------------------------------------------------------*/ |
| 610 | int dm_kcopyd_client_create(unsigned int nr_pages, | 639 | int dm_kcopyd_client_create(unsigned min_pages, |
| 611 | struct dm_kcopyd_client **result) | 640 | struct dm_kcopyd_client **result) |
| 612 | { | 641 | { |
| 613 | int r = -ENOMEM; | 642 | int r = -ENOMEM; |
| @@ -633,12 +662,12 @@ int dm_kcopyd_client_create(unsigned int nr_pages, | |||
| 633 | goto bad_workqueue; | 662 | goto bad_workqueue; |
| 634 | 663 | ||
| 635 | kc->pages = NULL; | 664 | kc->pages = NULL; |
| 636 | kc->nr_pages = kc->nr_free_pages = 0; | 665 | kc->nr_reserved_pages = kc->nr_free_pages = 0; |
| 637 | r = client_alloc_pages(kc, nr_pages); | 666 | r = client_reserve_pages(kc, min_pages); |
| 638 | if (r) | 667 | if (r) |
| 639 | goto bad_client_pages; | 668 | goto bad_client_pages; |
| 640 | 669 | ||
| 641 | kc->io_client = dm_io_client_create(nr_pages); | 670 | kc->io_client = dm_io_client_create(min_pages); |
| 642 | if (IS_ERR(kc->io_client)) { | 671 | if (IS_ERR(kc->io_client)) { |
| 643 | r = PTR_ERR(kc->io_client); | 672 | r = PTR_ERR(kc->io_client); |
| 644 | goto bad_io_client; | 673 | goto bad_io_client; |
