Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm kcopyd: alloc pages from the main page allocator

This patch changes dm-kcopyd so that it allocates pages from the main
page allocator with __GFP_NOWARN | __GFP_NORETRY flags (so that it can
fail in case of memory pressure). If the allocation fails, dm-kcopyd
allocates pages from its own reserve.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>

authored by

Mikulas Patocka and committed by
Alasdair G Kergon
d0471458 f99b55ee

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