#include <linux/module.h>
#include <linux/sysctl.h>
#include <linux/slab.h>
#include <litmus/sched_trace.h>
#include <litmus/color.h>
extern int color_sysctl_add_pages_data; /* litmus/color.c */
static int zero = 0;
static int one = 1;
/* used as names for server proc entries */
static char *period_str = "period";
static char *wcet_str = "wcet";
/* servers have a WCET and period */
#define NR_SERVER_PARAMS 2
#define CPU_NAME_LEN 3
struct color_cpu_server {
char name[CPU_NAME_LEN];
unsigned long wcet;
unsigned long period;
/* the + 1 is for the sentinel element */
struct ctl_table table[NR_SERVER_PARAMS + 1];
};
static struct color_cpu_server color_cpu_servers[NR_CPUS];
/* the + 1 is for the sentinel element */
static struct ctl_table color_cpu_tables[NR_CPUS + 1];
unsigned long color_chunk;
#define INFO_BUFFER_SIZE 100
static char info_buffer[100];
#define NR_PAGES_INDEX 0 /* location of nr_pages in the table below */
static struct ctl_table color_table[] =
{
{
/* you MUST update NR_PAGES_INDEX if you move this entry */
.procname = "nr_pages",
.mode = 0444,
.proc_handler = color_nr_pages_handler,
.data = NULL, /* dynamically set later */
.maxlen = 0, /* also set later */
},
{
.procname = "servers",
.mode = 0555,
.child = color_cpu_tables,
},
{
.procname = "add_pages",
.data = &color_sysctl_add_pages_data,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = color_add_pages_handler,
.extra1 = &zero,
.extra2 = &one,
},
{
.procname = "cache_info",
.mode = 0444,
.proc_handler = proc_dostring,
.data = info_buffer,
.maxlen = INFO_BUFFER_SIZE,
},
{
.procname = "chunk_size",
.mode = 0666,
.proc_handler = proc_doulongvec_minmax,
.data = &color_chunk,
.maxlen = sizeof(color_chunk),
},
{ }
};
static struct ctl_table litmus_table[] =
{
{
.procname = "color",
.mode = 0555,
.child = color_table,
},
{ }
};
static struct ctl_table litmus_dir_table[] = {
{
.procname = "litmus",
.mode = 0555,
.child = litmus_table,
},
{ }
};
int color_server_params(int cpu, unsigned long *wcet, unsigned long *period)
{
struct color_cpu_server *svr;
if (cpu >= num_online_cpus()) {
printk(KERN_WARNING "Cannot access illegal CPU: %d\n", cpu);
return -EFAULT;
}
svr = &color_cpu_servers[cpu];
if (svr->wcet == 0 || svr->period == 0) {
printk(KERN_WARNING "Server %d is uninitialized!\n", cpu);
return -EPERM;
}
*wcet = svr->wcet;
*period = svr->period;
TRACE("For %d: %lu, %lu\n", cpu, svr->wcet, svr->period);
return 0;
}
/* must be called AFTER nr_colors is set */
static int __init init_sysctl_nr_colors(void)
{
int ret = 0, maxlen = ONE_COLOR_LEN * color_cache_info.nr_colors;
color_table[NR_PAGES_INDEX].data = kmalloc(maxlen, GFP_KERNEL);
if (!color_table[NR_PAGES_INDEX].data) {
printk(KERN_WARNING "Could not allocate nr_pages buffer.\n");
ret = -ENOMEM;
goto out;
}
color_table[NR_PAGES_INDEX].maxlen = maxlen;
out:
return ret;
}
static void __init init_server_entry(struct ctl_table *entry,
unsigned long *parameter,
char *name)
{
entry->procname = name;
entry->mode = 0666;
entry->proc_handler = proc_doulongvec_minmax;
entry->data = parameter;
entry->maxlen = sizeof(*parameter);
}
static int __init init_cpu_entry(struct ctl_table *cpu_table,
struct color_cpu_server *svr, int cpu)
{
struct ctl_table *entry = svr->table;
printk(KERN_INFO "Creating cpu %d\n", cpu);
init_server_entry(entry, &svr->wcet, wcet_str);
entry++;
init_server_entry(entry, &svr->period, period_str);
/* minus one for the null byte */
snprintf(svr->name, CPU_NAME_LEN - 1, "%d", cpu);
cpu_table->procname = svr->name;
cpu_table->mode = 0555;
cpu_table->child = svr->table;
return 0;
}
static int __init init_server_entries(void)
{
int cpu, err = 0;
struct ctl_table *cpu_table;
struct color_cpu_server *svr;
for_each_online_cpu(cpu) {
cpu_table = &color_cpu_tables[cpu];
svr = &color_cpu_servers[cpu];
err = init_cpu_entry(cpu_table, svr, cpu);
if (err)
goto out;
}
out:
return err;
}
static struct ctl_table_header *litmus_sysctls;
static int __init litmus_sysctl_init(void)
{
int ret = 0;
printk(KERN_INFO "Registering LITMUS^RT proc sysctl.\n");
litmus_sysctls = register_sysctl_table(litmus_dir_table);
if (!litmus_sysctls) {
printk(KERN_WARNING "Could not register LITMUS^RT sysctl.\n");
ret = -EFAULT;
goto out;
}
ret = init_sysctl_nr_colors();
if (ret)
goto out;
ret = init_server_entries();
if (ret)
goto out;
snprintf(info_buffer, INFO_BUFFER_SIZE,
"Cache size\t: %lu B\n"
"Line size\t: %lu B\n"
"Page size\t: %lu B\n"
"Ways\t\t: %lu\n"
"Sets\t\t: %lu\n"
"Colors\t\t: %lu",
color_cache_info.size, color_cache_info.line_size, PAGE_SIZE,
color_cache_info.ways, color_cache_info.sets,
color_cache_info.nr_colors);
out:
return ret;
}
module_init(litmus_sysctl_init);