diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2011-05-29 08:03:07 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2011-05-29 08:03:07 -0400 |
commit | d04714580f12379fcf7a0f799e86c92b96dd4e1f (patch) | |
tree | 3a6c152dcb0d062dadfecd9fb8b1866a0fdbfce5 /drivers/md/dm-kcopyd.c | |
parent | f99b55eec795bd0fd577ab3ca06f3acfbe3b1ab1 (diff) |
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>
Diffstat (limited to 'drivers/md/dm-kcopyd.c')
-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; |