1 /******************************************************************************/
2 /* */
3 /* Copyright (c) Tejun Heo <tj@kernel.org>, 2009 */
4 /* */
5 /* This program is free software; you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation; either version 2 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
13 /* the GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program; if not, write to the Free Software */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 /* */
19 /******************************************************************************/
20 #include <linux/module.h>
21 #include <linux/device.h>
22 #include <linux/sysfs.h>
23 #include <linux/percpu.h>
24
pcpu_dump_chunk_slots(void)25 static inline void pcpu_dump_chunk_slots(void)
26 {
27 }
28
29 struct alloc_cmd {
30 size_t size;
31 int marker;
32 };
33
34 static const struct alloc_cmd cmds[] = {
35 {256, 0}, {256, 1}, {256, 0}, {256, 1},
36 {256, 0}, {256, 1}, {256, 0}, {256, 1},
37 {256, 0}, {256, 1}, {256, 0}, {256, 1},
38 {256, 0}, {256, 1}, {256, 0}, {256, 1},
39 {256, 0}, {256, 1}, {256, 0}, {256, 1},
40 {256, 0}, {256, 1}, {256, 0}, {256, 1},
41 {256, 0}, {256, 1}, {256, 0}, {256, 1},
42 {256, 0}, {256, 1}, {256, 0}, {256, 1}, /* 8k */
43
44 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
45 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
46 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
47 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
48 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
49 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
50 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2},
51 {1024, 2}, {1024, 2}, {1024, 2}, {1024, 2}, /* 32k */
52
53 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
54 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
55 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
56 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
57 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
58 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
59 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3},
60 {8192, 3}, {8192, 3}, {8192, 3}, {8192, 3}, /* 128k */
61
62 {0, 0}, /* free 0s */
63
64 {128, 4}, {128, 4}, {128, 4}, {128, 4},
65 {128, 4}, {128, 4}, {128, 4}, {128, 4},
66 {128, 4}, {128, 4}, {128, 4}, {128, 4},
67 {128, 4}, {128, 4}, {128, 4}, {128, 4},
68 {128, 4}, {128, 4}, {128, 4}, {128, 4},
69 {128, 4}, {128, 4}, {128, 4}, {128, 4},
70 {128, 4}, {128, 4}, {128, 4}, {128, 4},
71 {128, 4}, {128, 4}, {128, 4}, {128, 4},
72 {128, 4}, {128, 4}, {128, 4}, {128, 4}, /* 4.5k */
73
74 {0, 1}, /* free 1s */
75
76 {1024, 5}, {1024, 5}, {1024, 5}, {1024, 5}, /* 4k */
77
78 {0, 5}, /* free 5s */
79 {0, 4},
80 {0, 3},
81 {0, 2},
82 };
83
84 #define NR_CMDS ARRAY_SIZE(cmds)
85
86 static void *ptrs[NR_CMDS];
87
seed_val(unsigned int cmdno,unsigned int cpu)88 static unsigned long seed_val(unsigned int cmdno, unsigned int cpu)
89 {
90 return 0xdeadbeefbeefdeadULL
91 + cmdno + (cmdno << 16) + (cpu << 8) + (cpu << 24);
92 }
93
fill_area(void * p,size_t size,unsigned int cmdno)94 static void fill_area(void *p, size_t size, unsigned int cmdno)
95 {
96 unsigned int cpu;
97
98 for_each_possible_cpu(cpu) {
99 unsigned long v = seed_val(cmdno, cpu);
100 unsigned long *up = per_cpu_ptr(p, cpu);
101 size_t left = size;
102
103 while (left >= sizeof(unsigned long)) {
104 *up++ = v++;
105 left -= sizeof(unsigned long);
106 }
107 }
108 }
109
verify_area(void * p,size_t size,unsigned int cmdno)110 static void verify_area(void *p, size_t size, unsigned int cmdno)
111 {
112 unsigned int warns = 5;
113 unsigned int cpu;
114
115 for_each_possible_cpu(cpu) {
116 unsigned long v = seed_val(cmdno, cpu);
117 unsigned long *up = per_cpu_ptr(p, cpu);
118 size_t left = size;
119
120 while (left >= sizeof(unsigned long)) {
121 if (*up != v && warns-- > 0) {
122 printk
123 ("MISMATCH: cmdno=%u size=%zu cpu=%u off=%zu p=%p\n",
124 cmdno, size, cpu, size - left, p);
125 printk(" [%p]=%lx should be %lx\n", up,
126 *up, v);
127 }
128 up++;
129 v++;
130 left -= sizeof(unsigned long);
131 }
132 }
133 }
134
free_cmd(unsigned int cmdno)135 static void free_cmd(unsigned int cmdno)
136 {
137 if (!ptrs[cmdno])
138 return;
139
140 verify_area(ptrs[cmdno], cmds[cmdno].size, cmdno);
141 free_percpu(ptrs[cmdno]);
142 ptrs[cmdno] = NULL;
143 }
144
run_test(void)145 static void run_test(void)
146 {
147 unsigned int i, j;
148
149 for (i = 0; i < NR_CMDS; i++) {
150 const struct alloc_cmd *cmd = &cmds[i];
151
152 if (cmd->size) {
153 printk("ALLOC: %zu bytes marker=%d\n",
154 cmd->size, cmd->marker);
155 ptrs[i] = __alloc_percpu(cmd->size,
156 __alignof__(unsigned long
157 long));
158 if (ptrs[i])
159 fill_area(ptrs[i], cmd->size, i);
160 else
161 printk("failed to allocate %zu bytes\n",
162 cmd->size);
163 continue;
164 }
165
166 printk("FREE: marker=%d\n", cmd->marker);
167 pcpu_dump_chunk_slots();
168 for (j = 0; j < i; j++)
169 if (cmds[j].marker == cmd->marker)
170 free_cmd(j);
171 printk("FREE: done\n");
172 pcpu_dump_chunk_slots();
173 }
174 }
175
176 struct stupid_large_alignment_struct {
177 int a;
178 int b;
179 int c;
180 } __aligned(2048);
181
182 struct stupid_large_struct {
183 char a[1024];
184 };
185
186 static DEFINE_PER_CPU(struct stupid_large_alignment_struct, blah_blah);
187 DEFINE_PER_CPU(struct stupid_large_struct, blah_blah2);
188 DEFINE_PER_CPU(struct stupid_large_struct, blah_blah3);
189 DEFINE_PER_CPU(struct stupid_large_struct, blah_blah4);
190 DEFINE_PER_CPU(struct stupid_large_struct, blah_blah5);
191 DEFINE_PER_CPU(struct stupid_large_struct, blah_blah6);
192
test_init(void)193 static int __init test_init(void)
194 {
195 unsigned int cpu;
196
197 printk("XXX test_pcpu:");
198 for_each_possible_cpu(cpu)
199 printk(" %p", &per_cpu(blah_blah, cpu));
200 printk("\n");
201 pcpu_dump_chunk_slots();
202
203 run_test();
204 return 0;
205 }
206
test_exit(void)207 static void __exit test_exit(void)
208 {
209 unsigned int i;
210
211 printk("XXX cleaning up\n");
212 pcpu_dump_chunk_slots();
213
214 for (i = 0; i < NR_CMDS; i++)
215 free_cmd(i);
216
217 printk("XXX done\n");
218 pcpu_dump_chunk_slots();
219 }
220
221 module_init(test_init);
222 module_exit(test_exit);
223 MODULE_LICENSE("GPL");
224