userspace program code:
-----------------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#define GRID_SIZE 25
#define TOTAL_GRID_SIZE GRID_SIZE * (GRID_SIZE + 1) + 1
#define KLIFE_IOCTL_BASE 'L'
#define KLIFE_GET_GENERATION _IOW(KLIFE_IOCTL_BASE, 0, unsigned long)
#define KLIFE_SET_TIMER_MODE _IOR(KLIFE_IOCTL_BASE, 1, int)
struct grid {
unsigned char a[GRID_SIZE][GRID_SIZE];
};
int
main(void)
{
int fd;
int n, i, j;
int coord[] = {1, 1};
char buf[TOTAL_GRID_SIZE];
int enable = 1;
unsigned long gen;
fd = open("/dev/klife", O_RDWR);
if (fd < 0) {
perror("open error");
return 1;
}
for (i = 0; i < 5; i++) {
if (ioctl(fd, KLIFE_GET_GENERATION, &gen) != 0) {
perror("ioctl error");
}
printf("generation: %lu\n", gen);
n = read(fd, buf, TOTAL_GRID_SIZE);
if (n != TOTAL_GRID_SIZE)
perror("read error");
printf("%s\n", buf);
memset(buf, 0, TOTAL_GRID_SIZE);
}
return 0;
}
kernel-module code:
---------------------------
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <asm/page.h>
#include <linux/spinlock.h>
#include "klife.h"
#define klife_debug(fmt, arg...) \
printk(KERN_ALERT fmt, ##arg)
static int klife_major = 0;
static inline void
klife_timer_irq_handler(void *data)
{
return;
}
static int
init_klife(struct klife *k)
{
int ret;
spin_lock_init(&k->klife_lock);
ret = -ENOMEM;
/* one page to be exported to userspace, we zero it out for security */
k->grid = (void *) get_zeroed_page(GFP_KERNEL);
if (!k->grid)
goto done;
memset(k->grid, 1, sizeof(*k->grid));
/* this never goes to usrespace */
k->tmpgrid = kmalloc(sizeof(*k->tmpgrid), GFP_KERNEL);
if (!k->tmpgrid)
goto free_grid;
k->timer_hook.func = klife_timer_irq_handler;
k->timer_hook.data = "">
register_timer_interrupt(&k->timer_hook);
return 0;
free_grid:
free_page((unsigned long)k->grid);
done:
return ret;
}
static int
klife_alloc(struct klife **pk)
{
int ret;
struct klife *k;
k = kmalloc(sizeof(*k), GFP_KERNEL);
if (!k)
return -ENOMEM;
memset(k, 0, sizeof(*k));
ret = init_klife(k);
if (ret)
kfree(k);
*pk = k;
return ret;
}
static int
klife_open(struct inode* indode, struct file* filp)
{
int ret;
struct klife *k;
ret = klife_alloc(&k);
if (ret)
return ret;
filp->private_data = k;
klife_debug("klife_open done\n");
return 0;
}
static int
valid_coord(int x, int y)
{
if (x >= 0 && x <= GRID_SIZE - 1 && y >= 0 && y <= GRID_SIZE - 1)
return 1;
return 0;
}
static void
set_cell_alive(struct grid *grid, int x, int y)
{
grid->a[x][y] = 1;
}
static void
set_cell_dead(struct grid *grid, int x, int y)
{
grid->a[x][y] = 0;
}
static int
cell_is_alive(struct grid *grid, int i, int j)
{
return (grid->a[i][j] == 1);
}
static int
cell_is_dead(struct grid *grid, int i, int j)
{
return (grid->a[i][j] == 0);
}
static int
klife_add_position(struct klife *k, char *data, int count)
{
int x, y;
printk(KERN_ALERT "%d %d\n", count, sizeof(x) + sizeof(y));
if (count != (sizeof(x) + sizeof(y)))
return -E2BIG; /* argument size too long */
memcpy(&x, data, sizeof(x));
memcpy(&y, data + sizeof(x), sizeof(y));
klife_debug("klife_add_position: cord (%d,%d)\n", x, y);
if (!valid_coord(x, y))
return -EINVAL;
set_cell_alive(k->grid, x, y);
return 0;
}
static int
klife_neighbours(struct klife *klife, int i, int j)
{
int i1, j1;
int n = 0;
for (i1 = i - 1; i1 <= i + 1; i1++) {
if (i1 < 0 || i1 >= GRID_SIZE)
continue;
for(j1 = j - 1; j1 <= j + 1; j1++) {
if (j1 < 0 || j1 >= GRID_SIZE)
continue;
if (j1 == j && i1 == i)
continue;
if (cell_is_alive(klife->grid, i1, j1)) {
n++;
}
}
}
return n;
}
static void
klife_next_generation(struct klife *klife)
{
int i, j, n;
for (i = 0; i < GRID_SIZE; i++) {
for (j = 0; j < GRID_SIZE; j++) {
n = klife_neighbours(klife, i, j);
if (cell_is_dead(klife->grid, i, j) && (n == 3))
set_cell_alive(klife->tmpgrid, i, j);
else if (cell_is_alive(klife->grid, i, j) && ((n == 2) || (n == 3)))
set_cell_alive(klife->tmpgrid, i, j);
else
set_cell_dead(klife->tmpgrid, i, j);
}
}
memcpy(klife->grid, klife->tmpgrid, sizeof(*klife->grid));
klife->generation++;
}
static int
klife_draw(struct klife *klife, char *page)
{
int i, j;
char *orig = page;
ssize_t required_mem;
required_mem = (GRID_SIZE) * (GRID_SIZE + 1) + 1;
if (required_mem > PAGE_SIZE)
return -ENOMEM;
for (i = 0; i < GRID_SIZE; i++) {
for (j = 0; j < GRID_SIZE; j++) {
*page++ = cell_is_alive(klife->grid, i, j)? 'A': 'D';
}
*page++ = '\n';
}
*page++ = '\0';
return (page - orig);
}
static ssize_t
klife_read_mapped(struct file *filp, char *ubuf, size_t count, loff_t *f_pos)
{
struct klife *klife;
unsigned long flags;
klife = filp->private_data;
spin_lock_irqsave(&klife->klife_lock, flags);
klife_next_generation(klife);
spin_unlock_irqrestore(&klife->klife_lock, flags);
return min(PAGE_SIZE, count);
}
static int
klife_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long data)
{
int ret;
unsigned long gen;
unsigned long flags;
struct klife *klife = filp->private_data;
ret = 0;
switch (cmd) {
case KLIFE_GET_GENERATION:
printk("KLIFE_GET_GENERATION\n");
spin_lock_irqsave(&klife->klife_lock, flags);
gen = klife->generation;
spin_unlock_irqrestore(&klife->klife_lock, flags);
if (copy_to_user((void *) data, &gen, sizeof(gen))) {
ret = -EFAULT;
goto done;
}
}
done:
return ret;
}
static ssize_t
klife_read(struct file *filp, char *ubuf, size_t count, loff_t *f_pos)
{
int ret;
size_t len = 0;
struct klife *klife;
unsigned long flags;
char *page;
klife = filp->private_data;
printk("klife->mapped; %d\n", klife->mapped);
/* for memory mapped read */
if (klife->mapped)
return klife_read_mapped(filp, ubuf, count, f_pos);
/* allocate page to draw grid on it */
page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
spin_lock_irqsave(&klife->klife_lock, flags);
klife_next_generation(klife);
len = klife_draw(klife, page);
spin_unlock_irqrestore(&klife->klife_lock, flags);
len = min(count, (size_t) len);
if (copy_to_user(ubuf, page, len)) {
ret = -EFAULT;
goto free_page;
}
*f_pos += len;
ret = len;
free_page:
kfree(page);
return ret;
}
static ssize_t
klife_write(struct file *filp, const char __user *ubuf, size_t count,
loff_t *f_pos)
{
int ret;
ssize_t sz;
char *kbuf;
sz = count > PAGE_SIZE? PAGE_SIZE: count;
kbuf = kmalloc(sz, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
ret = -EFAULT;
if (copy_from_user(kbuf, ubuf, sz))
goto free_buf;
/* don't we need lock here? */
ret = klife_add_position(filp->private_data, kbuf, sz);
if (!ret)
ret = sz;
free_buf:
kfree(kbuf);
return ret;
}
static int
klife_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret = 0;
struct klife *klife = filp->private_data;
klife_debug("vma: %p vma->vm_start 0x%lx vma->vm_end 0x%lx\n", vma,
vma->vm_start, vma->vm_end);
if (vma->vm_flags & VM_SHARED)
return -EINVAL; /* we don't support VM_SHARED */
if (vma->vm_end - vma->vm_start < sizeof(*klife->grid))
return -ENOSPC;
SetPageReserved(virt_to_page(klife->grid));
/* CHECK: mapping is created based on content when it was mapped,
so if we access through pointer, instead of read/write
we won't get updated info?
*/
ret = remap_pfn_range(vma, vma->vm_start,
page_to_pfn(virt_to_page(klife->grid)), PAGE_SIZE,
vma->vm_page_prot);
if (!ret)
klife->mapped = 1;
return ret;
}
static void
free_klife(struct klife *k)
{
free_page((unsigned long) k->grid);
kfree(k->tmpgrid);
kfree(k);
}
static int
klife_release(struct inode *inode, struct file *filp)
{
struct klife *klife = filp->private_data;
/* this is very important, otherwise page won't be unmapped,
kernel oops */
if (klife->mapped)
ClearPageReserved(virt_to_page(klife->grid));
free_klife(klife);
klife_debug("klife_release done\n");
return 0;
}
struct file_operations klife_ops = {
.owner = THIS_MODULE,
.open = klife_open,
.read = klife_read,
.write = klife_write,
.mmap = klife_mmap,
.ioctl = klife_ioctl,
.release = klife_release
};
static void
__exit klife_module_exit(void)
{
unregister_chrdev(klife_major, "klife");
klife_debug("klife_module_exit done\n");
}
static int
__init klife_module_init(void)
{
int ret = 0;
ret = register_chrdev(klife_major, "klife", &klife_ops);
if (ret < 0) {
printk(KERN_ERR "register_chrdev() error\n");
}
klife_major = ret;
klife_debug("klife module registered with major: %d\n", klife_major);
klife_debug("klife_module_init done\n");
return ret;
}
module_init(klife_module_init);
module_exit(klife_module_exit);
Thanks,
Meraj
Mulyadi Santosa <mulyadi.santosa@xxxxxxxxx> wrote:
Hi..
> I made all my register functions inline and also replaced xchg with spin_lock and spin_unlock. But that did not help. I am having the same result.
>
>
Thank you for considering my suggestions. At least we are one step
closer to the solution. So it's not about being inlined or not nor
improper usage of xchg().
> To clarify the confusion, I actually run a userspace program that calls the module's open routine which registers the hook. So registration is actually done by the module.
>
Could you show us your userspace code? because...maybe, just maybe, the
problem lies in there.
> However, this time I have something for you. I ran the module and program from console instead of running in GUI and it looks like the kernel actually panics when it freezes the system. Part of the output I could write down goes below -
>
> ----------------------------------------
> EIP: [<60455670>] free_fdtable_rcu + 0xd/0x75 SS:ESP
> 0068: f5c71f10
> kernel_panic - not syncing: Fatal exception in interrupt
> -----------------------------------------
>
>
Hmmm, RCU.... And on the other hand, it clearly shows us there is
"exception" although it's not clear what kind of exception it was.
> This happens with both 4k and 8k stacks with/without inline register functions.
>
>
OK, not stack problem. In general, all my theories are proven wrong.
let's find another suspect..
regards,
Mulyadi
Need a vacation? Get great deals to amazing places on Yahoo! Travel.