blob: 550edc99d07fe20b1bf25491b689f8a2386ce336 [file] [log] [blame]
/******************************************************************************/
/* */
/* Copyright (c) Tejun Heo <tj@kernel.org>, 2009 */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/******************************************************************************/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/percpu.h>
static inline void pcpu_dump_chunk_slots(void) {}
struct alloc_cmd {
size_t size;
int marker;
};
static const struct alloc_cmd cmds[] = {
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 },
{ 256, 0 }, { 256, 1 }, { 256, 0 }, { 256, 1 }, /* 8k */
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 },
{ 1024, 2 }, { 1024, 2 }, { 1024, 2 }, { 1024, 2 }, /* 32k */
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 },
{ 8192, 3 }, { 8192, 3 }, { 8192, 3 }, { 8192, 3 }, /* 128k */
{ 0, 0 }, /* free 0s */
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 },
{ 128, 4 }, { 128, 4 }, { 128, 4 }, { 128, 4 }, /* 4.5k */
{ 0, 1 }, /* free 1s */
{ 1024, 5 }, { 1024, 5 }, { 1024, 5 }, { 1024, 5 }, /* 4k */
{ 0, 5 }, /* free 5s */
{ 0, 4 },
{ 0, 3 },
{ 0, 2 },
};
#define NR_CMDS ARRAY_SIZE(cmds)
static void *ptrs[NR_CMDS];
static unsigned long seed_val(unsigned int cmdno, unsigned int cpu)
{
return 0xdeadbeefbeefdeadULL
+ cmdno + (cmdno << 16) + (cpu << 8) + (cpu << 24);
}
static void fill_area(void *p, size_t size, unsigned int cmdno)
{
unsigned int cpu;
for_each_possible_cpu(cpu) {
unsigned long v = seed_val(cmdno, cpu);
unsigned long *up = per_cpu_ptr(p, cpu);
size_t left = size;
while (left >= sizeof(unsigned long)) {
*up++ = v++;
left -= sizeof(unsigned long);
}
}
}
static void verify_area(void *p, size_t size, unsigned int cmdno)
{
unsigned int warns = 5;
unsigned int cpu;
for_each_possible_cpu(cpu) {
unsigned long v = seed_val(cmdno, cpu);
unsigned long *up = per_cpu_ptr(p, cpu);
size_t left = size;
while (left >= sizeof(unsigned long)) {
if (*up != v && warns-- > 0) {
printk("MISMATCH: cmdno=%u size=%zu cpu=%u off=%zu p=%p\n",
cmdno, size, cpu, size - left, p);
printk(" [%p]=%lx should be %lx\n",
up, *up, v);
}
up++; v++;
left -= sizeof(unsigned long);
}
}
}
static void free_cmd(unsigned int cmdno)
{
if (!ptrs[cmdno])
return;
verify_area(ptrs[cmdno], cmds[cmdno].size, cmdno);
free_percpu(ptrs[cmdno]);
ptrs[cmdno] = NULL;
}
static void run_test(void)
{
unsigned int i, j;
for (i = 0; i < NR_CMDS; i++) {
const struct alloc_cmd *cmd = &cmds[i];
if (cmd->size) {
printk("ALLOC: %zu bytes marker=%d\n",
cmd->size, cmd->marker);
ptrs[i] = __alloc_percpu(cmd->size,
__alignof__(unsigned long long));
if (ptrs[i])
fill_area(ptrs[i], cmd->size, i);
else
printk("failed to allocate %zu bytes\n",
cmd->size);
continue;
}
printk("FREE: marker=%d\n", cmd->marker);
pcpu_dump_chunk_slots();
for (j = 0; j < i; j++)
if (cmds[j].marker == cmd->marker)
free_cmd(j);
printk("FREE: done\n");
pcpu_dump_chunk_slots();
}
}
struct stupid_large_alignment_struct {
int a;
int b;
int c;
} __aligned(2048);
struct stupid_large_struct {
char a[1024];
};
static DEFINE_PER_CPU(struct stupid_large_alignment_struct, blah_blah);
DEFINE_PER_CPU(struct stupid_large_struct, blah_blah2);
DEFINE_PER_CPU(struct stupid_large_struct, blah_blah3);
DEFINE_PER_CPU(struct stupid_large_struct, blah_blah4);
DEFINE_PER_CPU(struct stupid_large_struct, blah_blah5);
DEFINE_PER_CPU(struct stupid_large_struct, blah_blah6);
static int __init test_init(void)
{
unsigned int cpu;
printk("XXX test_pcpu:");
for_each_possible_cpu(cpu)
printk(" %p", &per_cpu(blah_blah, cpu));
printk("\n");
pcpu_dump_chunk_slots();
run_test();
return 0;
}
static void __exit test_exit(void)
{
unsigned int i;
printk("XXX cleaning up\n");
pcpu_dump_chunk_slots();
for (i = 0; i < NR_CMDS; i++)
free_cmd(i);
printk("XXX done\n");
pcpu_dump_chunk_slots();
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");