aboutsummaryrefslogtreecommitdiffstats
path: root/block/partitions
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2013-02-27 20:05:19 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-27 22:10:21 -0500
commitac2e5327a5e4f6477afc6a3b3b0dc6e0476d71d4 (patch)
treee8193241c1789655f2168013d1ee434c21f12bac /block/partitions
parent06004e6eebdaf101a0f9b533fac77b5768d2c09c (diff)
block/partitions: optimize memory allocation in check_partition()
Currently, sizeof(struct parsed_partitions) may be 64KB in 32bit arch, so it is easy to trigger page allocation failure by check_partition, especially in hotplug block device situation(such as, USB mass storage, MMC card, ...), and Felipe Balbi has observed the failure. This patch does below optimizations on the allocation of struct parsed_partitions to try to address the issue: - make parsed_partitions.parts as pointer so that the pointed memory can fit in 32KB buffer, then approximate 32KB memory can be saved - vmalloc the buffer pointed by parsed_partitions.parts because 32KB is still a bit big for kmalloc - given that many devices have the partition count limit, so only allocate disk_max_parts() partitions instead of 256 partitions always Signed-off-by: Ming Lei <ming.lei@canonical.com> Reported-by: Felipe Balbi <balbi@ti.com> Cc: Jens Axboe <axboe@kernel.dk> Reviewed-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'block/partitions')
-rw-r--r--block/partitions/check.c37
-rw-r--r--block/partitions/check.h4
2 files changed, 35 insertions, 6 deletions
diff --git a/block/partitions/check.c b/block/partitions/check.c
index bc908672c976..19ba207ea7d1 100644
--- a/block/partitions/check.c
+++ b/block/partitions/check.c
@@ -14,6 +14,7 @@
14 */ 14 */
15 15
16#include <linux/slab.h> 16#include <linux/slab.h>
17#include <linux/vmalloc.h>
17#include <linux/ctype.h> 18#include <linux/ctype.h>
18#include <linux/genhd.h> 19#include <linux/genhd.h>
19 20
@@ -106,18 +107,45 @@ static int (*check_part[])(struct parsed_partitions *) = {
106 NULL 107 NULL
107}; 108};
108 109
110static struct parsed_partitions *allocate_partitions(struct gendisk *hd)
111{
112 struct parsed_partitions *state;
113 int nr;
114
115 state = kzalloc(sizeof(*state), GFP_KERNEL);
116 if (!state)
117 return NULL;
118
119 nr = disk_max_parts(hd);
120 state->parts = vzalloc(nr * sizeof(state->parts[0]));
121 if (!state->parts) {
122 kfree(state);
123 return NULL;
124 }
125
126 state->limit = nr;
127
128 return state;
129}
130
131void free_partitions(struct parsed_partitions *state)
132{
133 vfree(state->parts);
134 kfree(state);
135}
136
109struct parsed_partitions * 137struct parsed_partitions *
110check_partition(struct gendisk *hd, struct block_device *bdev) 138check_partition(struct gendisk *hd, struct block_device *bdev)
111{ 139{
112 struct parsed_partitions *state; 140 struct parsed_partitions *state;
113 int i, res, err; 141 int i, res, err;
114 142
115 state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL); 143 state = allocate_partitions(hd);
116 if (!state) 144 if (!state)
117 return NULL; 145 return NULL;
118 state->pp_buf = (char *)__get_free_page(GFP_KERNEL); 146 state->pp_buf = (char *)__get_free_page(GFP_KERNEL);
119 if (!state->pp_buf) { 147 if (!state->pp_buf) {
120 kfree(state); 148 free_partitions(state);
121 return NULL; 149 return NULL;
122 } 150 }
123 state->pp_buf[0] = '\0'; 151 state->pp_buf[0] = '\0';
@@ -128,10 +156,9 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
128 if (isdigit(state->name[strlen(state->name)-1])) 156 if (isdigit(state->name[strlen(state->name)-1]))
129 sprintf(state->name, "p"); 157 sprintf(state->name, "p");
130 158
131 state->limit = disk_max_parts(hd);
132 i = res = err = 0; 159 i = res = err = 0;
133 while (!res && check_part[i]) { 160 while (!res && check_part[i]) {
134 memset(&state->parts, 0, sizeof(state->parts)); 161 memset(state->parts, 0, state->limit * sizeof(state->parts[0]));
135 res = check_part[i++](state); 162 res = check_part[i++](state);
136 if (res < 0) { 163 if (res < 0) {
137 /* We have hit an I/O error which we don't report now. 164 /* We have hit an I/O error which we don't report now.
@@ -161,6 +188,6 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
161 printk(KERN_INFO "%s", state->pp_buf); 188 printk(KERN_INFO "%s", state->pp_buf);
162 189
163 free_page((unsigned long)state->pp_buf); 190 free_page((unsigned long)state->pp_buf);
164 kfree(state); 191 free_partitions(state);
165 return ERR_PTR(res); 192 return ERR_PTR(res);
166} 193}
diff --git a/block/partitions/check.h b/block/partitions/check.h
index 52b100311ec3..eade17ea910b 100644
--- a/block/partitions/check.h
+++ b/block/partitions/check.h
@@ -15,13 +15,15 @@ struct parsed_partitions {
15 int flags; 15 int flags;
16 bool has_info; 16 bool has_info;
17 struct partition_meta_info info; 17 struct partition_meta_info info;
18 } parts[DISK_MAX_PARTS]; 18 } *parts;
19 int next; 19 int next;
20 int limit; 20 int limit;
21 bool access_beyond_eod; 21 bool access_beyond_eod;
22 char *pp_buf; 22 char *pp_buf;
23}; 23};
24 24
25void free_partitions(struct parsed_partitions *state);
26
25struct parsed_partitions * 27struct parsed_partitions *
26check_partition(struct gendisk *, struct block_device *); 28check_partition(struct gendisk *, struct block_device *);
27 29