Related
I am learning how to write Linux Device Driver.
I wrote a dummy character device driver, implemented open, release, write, read in fops;
When I read from device , everything was ok;
When I wrote to device by "echo xx > ", the OS was hang.
Even I comment out all codes in write function except pr_alert and return statements, It still hangs;
Could anybody help me figure it out?
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
struct hello_dev
{
char *buffer;
int length;
dev_t dev;
struct mutex lock;
struct cdev *pcdev;
};
struct hello_dev *pHelloDev;
int open_device(struct inode *pinode, struct file *filp)
{
filp->private_data = pHelloDev;
return 0;
}
int close_device(struct inode *pinode, struct file *filp)
{
struct hello_dev *pDev = filp->private_data;
if (pDev->buffer != NULL)
kfree(pDev->buffer);
pDev->buffer = NULL;
return 0;
}
ssize_t read_device(struct file *filp, char __user *buffer, size_t len, loff_t *loff)
{
pr_alert("read\n");
struct hello_dev *pDev = filp->private_data;
mutex_lock(&pDev->lock);
if (pDev->buffer == NULL)
{
mutex_unlock(&pDev->lock);
return 0;
}
int length = strlen(pDev->buffer);
// offset max than strlen in buffer, return
if (*loff > (length - 1))
{
mutex_unlock(&pDev->lock);
return 0;
} else {
// available to read
int len2read = length - *loff;
if (len < len2read)
{// buffer length less than available data
len2read = len;
}
int read = copy_to_user(buffer, pDev->buffer + *loff, len2read);
if (read)
{
*loff = *loff + read;
mutex_unlock(&pDev->lock);
return read;
} else {
*loff = *loff + len2read;
mutex_unlock(&pDev->lock);
return len2read;
}
}
}
ssize_t write_device(struct file *filp , const char __user *buffer, size_t len, loff_t* loff) {
pr_alert("write %s\n", buffer);
// struct hello_dev *pDev = filp->private_data;
// mutex_lock(&pDev->lock);
// if(pDev->buffer == NULL) {
// pDev->buffer = kmalloc(100, GFP_KERNEL);
// pDev->length = 100;
// }
// copy_from_user(pDev->buffer, buffer, len);
// *loff = *loff + len;
// mutex_unlock(&pDev->lock);
return len;
}
struct file_operations fops = {
.open = open_device,
.release = close_device,
.read = read_device,
.write = write_device
};
int init_device(void)
{
pr_alert("init device\n");
pHelloDev = kmalloc(sizeof(struct hello_dev), GFP_KERNEL);
pHelloDev->buffer = NULL;
pHelloDev->length = 0;
int ret = alloc_chrdev_region(&pHelloDev->dev, 0, 1, "hello");
if (ret)
goto alloc_error;
if (pHelloDev == NULL)
goto kmalloc_error;
pHelloDev->pcdev = cdev_alloc();
pHelloDev->pcdev->ops = &fops;
mutex_init(&pHelloDev->lock);
ret = cdev_add(pHelloDev->pcdev, pHelloDev->dev, 1);
if (ret)
goto cdev_add_error;
return 0;
alloc_error:
pr_alert("alloc_chrdev_region error, %d\n", ret);
return ret;
kmalloc_error:
pr_alert("alloc struct hello_dev error");
return -ENOMEM;
cdev_add_error:
pr_alert("cdev_add error, %d\n", ret);
return ret;
}
void cleanup_device(void)
{
pr_alert("unload ko\n");
cdev_del(pHelloDev->pcdev);
unregister_chrdev_region(pHelloDev->dev, 1);
}
MODULE_LICENSE("GPL");
module_init(init_device);
module_exit(cleanup_device);
I found why write to device hangs.
//this statements has problem
//maybe there is no \0 in buffer
//so I print it out, it will hang
//I wrote a program to write something to device
//and used strace to trace system call made by this program
//and found it hangs at write(...) system call
//and there was nothing printed out
//so, it must be this statement causing the problem
//when I removed this statement, everything was ok
pr_alert("write %s\n", buffer);
I tried to execute a userspace binary inside the Linux kernel module with call_usermodehelper. I found the launched application is running in root mode. Is it possible to run the application in a user mode, for example, named user1?
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
// struct cred *new;
new = prepare_creds();
if (!new)
return -ENOMEM;
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return commit_creds(new);
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}
Based on Milag's comment, I tried to update the u[g/e/s]idin the init_func() with prepare_creds() and commit_creds(new). In the kernel log, I can see current->uid has changed to 1000. But when I run ps aux, the process is still in root mode. Any idea why?
After I read some kernel sources and posted comments above, OP later showed updates for a developed kmod.
Short answer: yes, it's possible to set different IDs for a user process launched from a kmod.
After passing an init routine to call_usermodehelper_setup(), a related kernel service calls the init routine with a (struct cred *); various uid and gid members can be changed there. For more details, see call_usermodehelper_exec_async()
There are related suggestions for kmod versatility:
add a pair of #define for a set of default uid and gid
add support for module params to set other uid and gid
optionally provide cmd-line params when the module is loaded
For an example, see this link .
Based on #Milag's comment, the following code makes the new userspace process running in user-mode (using ps aux to inspect):
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return 0;
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}
i am trying adding my own customized layer 4 protocol in linux (ubuntu 14.04) - IPPROTO_MYPROTO as a loadable kernel module. I have done all necessary steps to register the protocol. Here i am sharing my code.
When i am trying to send a mesage from user space program using sendmsg(), i expect the corresponding fn myproto_sendmsg() registered via struct proto structure should be called in kernel space. But what i am observing is that though the myproto_sendmsg() in kernel space is not being called, yet destination machine is receiving the correct data. surprise ! surprise !. Is the default udp sendmsg() fn kicking in here which is like uninvited guest doing his work.
Here, sendmsg() call in user space returns as many bytes as send. Hence, fn returns success.
User space program :
void forwardUDP( int destination_node ,char sendString[] )
{
struct msghdr msg;
destination_node = destination_node % N; //destination node to which data is to be forwaded
int sock, rc;
struct sockaddr_in server_addr;
struct iovec iov;
struct hostent *host; //hostent predefined structure use to store info about host
host = (struct hostent *) gethostbyname(node[destination_node].ip_address);//gethostbyname returns a pointer to hostent
if ((sock = socket(AF_INET, SOCK_RAW, 5)) == -1)
{
perror("socket");
exit(1);
}
//destination address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(node[destination_node].udpportno);
server_addr.sin_addr = *((struct in_addr *)host->h_addr); //host->h_addr gives address of host
bzero(&(server_addr.sin_zero),8);
/* fill the messsage structure*/
memset(&msg, 0 , sizeof(struct msghdr));
memset(&iov, 0, sizeof(struct iovec));
msg.msg_name = (void *)&server_addr;
msg.msg_namelen = sizeof(struct sockaddr_in);
printf("sendString = %s\n", sendString);
iov.iov_base = (void *)sendString;
iov.iov_len = strlen(sendString);
msg.msg_iov = &iov;
printf("len = %d\n", strlen(sendString));
#if 1
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
#endif
//sendto(sock, sendString, strlen(sendString), 0,(struct sockaddr *)&server_addr, sizeof(struct sockaddr));
**rc = sendmsg(sock, &msg, 0);**
printf("rc = %d\n", rc);
//sendto() function shall send a message through a connectionless-mode socket.
printf("\nFORWARD REQUEST : '%s' has been forwarded to node ---->%d\n",sendString,destination_node);
//close(sock);
}
Kernel Module
/* Define the handler which will recieve all ingress packets for protocol = IPPROTO_MYPROTO
defined in net/protocol.h
*/
/* Resgiter the call backs for pkt reception */
static const struct net_protocol myproto_protocol = {
.handler = myproto_rcv,
.err_handler = myproto_err,
.no_policy = 1,
.netns_ok = 1,
};
static struct inet_protosw myproto_protosw;
int
myproto_rcv(struct sk_buff *skb){
printk(KERN_INFO "myproto_rcv is called\n");
return 0;
}
int
myproto_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len){
printk(KERN_INFO "myproto_sendmsg() is called\n");
return 0;
}
void myproto_lib_close(struct sock *sk, long timeout){
printk(KERN_INFO "close is called\n");
return;
}
int
myproto_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg,
size_t len, int noblock, int flags,
int *addr_len){
printk(KERN_INFO "myproto_recvmsg() is called.\n");
printk(KERN_INFO "iocb = 0x%x,\nsk = 0x%x,\nmsg = 0x%x,\nlen = %d,\nnoblock = %d,\nflags = %d,\naddr_len = 0x%x", iocb, sk, msg, len, noblock, flags, addr_len);
return 0;
}
/* Socket structure for Custom protocol, see struct udp_sock for example*/
struct myproto_sock{
struct inet_sock inet; // should be first member
__u16 len;
};
void
myproto_lib_hash(struct sock *sk){
printk(KERN_INFO "myproto_lib_hash() is called");
}
/* Define the **struct proto** structure for the Custom protocol defined in
net/sock.h */
struct proto myproto_prot = {
.name = "MYPROTO",
.owner = THIS_MODULE,
.close = myproto_lib_close,
.sendmsg = myproto_sendmsg,
.hash = myproto_lib_hash,
.recvmsg = myproto_recvmsg,
.obj_size = sizeof(struct myproto_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
};
int init_module(void);
void cleanup_module(void);
int
init_module(void)
{
int rc = 0;
rc = proto_register(&myproto_prot, 1);
if(rc == 0){
printk(KERN_INFO "Protocol registration is successful\n");
}
else{
printk(KERN_INFO "Protocol registration is failed\n");
cleanup_module();
return rc;
}
rc = inet_add_protocol(&myproto_protocol, IPPROTO_MYPROTO);
if(rc == 0){
printk(KERN_INFO "Protocol insertion in inet_protos[] is successful\n");
}
else{
printk(KERN_INFO "Protocol insertion in inet_protos[] is failed\n");
cleanup_module();
return rc;
}
memset(&myproto_protosw, 0 ,sizeof(myproto_protosw));
myproto_protosw.type = SOCK_RAW;
myproto_protosw.protocol = IPPROTO_MYPROTO;
myproto_protosw.prot = &myproto_prot;
extern const struct proto_ops inet_dgram_ops; // defined in ipv4/af_inet.c
myproto_protosw.ops = &inet_dgram_ops;
myproto_protosw.flags = INET_PROTOSW_REUSE;
inet_register_protosw(&myproto_protosw);
return 0;
}
void cleanup_module(void)
{
int rc = 0;
rc = inet_del_protocol(&myproto_protocol, IPPROTO_MYPROTO);
if(rc == 0)
printk(KERN_INFO "Protocol removed successful\n");
else
printk(KERN_INFO "Protocol removal failed\n");
proto_unregister(&myproto_prot);
printk(KERN_INFO "Module cleaned up\n");
return;
}
I am writing kqueue hooks for a character device that allows a client to block waiting for an EVFILT_READ. If I set my read filters code to always return one the kevent will return instantly. However, if the filter returns one at some later point in time nothing unblocks. For the following code the printf "After" never happens and in the filter code I can trivially get "filter_Read return 1" (immediately followed by a return 0)
Device (relevant excerpt)
static int
lowmem_filter_read(struct knote *kn, long hint)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
if(manual_alert){
manual_alert=0;
printf("filter_Read return 1\n");
return 1;
}
printf("filter_Read return 0\n");
return 0;
}
static void
lowmem_filter_detach(struct knote *kn)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
knlist_remove(&kl, kn, 0);
}
static struct filterops lowmem_filtops_read = {
.f_isfd = 1,
.f_detach = lowmem_filter_detach,
.f_event = lowmem_filter_read,
};
static int
lowmem_kqfilter(struct cdev *dev, struct knote *kn)
{
int err = EINVAL;
/* Figure out who needs service */
lowmem_lock();
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &lowmem_filtops_read;
knlist_add(&kl, kn, 1);
err = 0;
break;
default:
err = EOPNOTSUPP;
break;
}
lowmem_unlock();
return (err);
}
Client:
struct kevent ev;
struct timespec nullts = {0,0};
int fd=0;
int main(int argc, char **argv){
fd = open("/dev/lowmem", O_RDWR | O_NONBLOCK);
int kq=kqueue();
EV_SET(&ev,fd,EVFILT_READ, EV_ADD,0,0,NULL);
kevent(kq,&ev,1,NULL,0,&nullts);
for(;;){
printf("Starting\n");
int n=kevent(kq,NULL,0,&ev,1,NULL);
printf("After\n");
if(n>0){
printf("Something happened ev.fflags=%i\n",(int)ev.fflags);
}
}
return 0;
}
I wrote a simple module which emulates a force feedback input device so that I can test the underlying FFB implementation. However, the module sometimes crashes the kernel with the "scheduling while atomic" message. According to the backtrace the crash is triggered by ff_dummy_open() and I can reproduce it easily by destroying and recreating the dummy device a couple of times in a row through sysfs. What am I doing wrong?
Source of the module:
#include <linux/input.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/input/ff-logitech.h>
MODULE_LICENSE("GPL");
struct ff_dummy_device {
struct input_dev *dev;
int slot;
};
static struct kobject *ff_dummy_kobj;
static struct ff_dummy_device **ff_dummy_devices;
static int max_device_count;
static unsigned char device_count = 0;
static struct mutex mtx;
static const char *ff_dummy_name = "DumFFBD";
static const signed short ff_dummy_effects[] = {
FF_CONSTANT,
FF_PERIODIC,
FF_TRIANGLE,
FF_SAW_UP,
FF_SAW_DOWN,
FF_SINE,
FF_SQUARE,
FF_SPRING,
FF_DAMPER,
FF_INERTIA,
FF_FRICTION,
FF_RAMP,
-1
};
static int ff_dummy_control(struct input_dev *dev, void *data, const struct lgff_effect_command *command)
{
struct ff_dummy_device *ddev = data;
switch (command->cmd) {
case LGFF_START_COMBINED:
pr_debug("Force X: %d, Force Y: %d, (idx %d)\n", command->u.simple_force.x, command->u.simple_force.y, ddev->slot);
break;
case LGFF_STOP_COMBINED:
pr_debug("Stopping COMBINED effect (idx %d).\n", ddev->slot);
break;
case LGFF_START_UNCOMB:
pr_debug("Starting UNCOMBINABLE effect.\n");
pr_debug("LC(x): %d RC(x): %d, LS(x): %d, RS(x): %d\n",
command->u.uncomb.effect->u.condition[0].left_coeff,
command->u.uncomb.effect->u.condition[0].right_coeff,
command->u.uncomb.effect->u.condition[0].left_saturation,
command->u.uncomb.effect->u.condition[0].right_saturation);
pr_debug("LC(y): %d RC(y): %d, LS(y): %d, RS(y): %d\n",
command->u.uncomb.effect->u.condition[1].left_coeff,
command->u.uncomb.effect->u.condition[1].right_coeff,
command->u.uncomb.effect->u.condition[1].left_saturation,
command->u.uncomb.effect->u.condition[1].right_saturation);
switch (command->u.uncomb.effect->type) {
case FF_DAMPER:
pr_debug("Starting DAMPER id %d\n", command->u.uncomb.id);
break;
case FF_FRICTION:
pr_debug("Starting FRICTION id %d\n", command->u.uncomb.id);
break;
case FF_INERTIA:
pr_debug("Starting INERTIA id %d\n", command->u.uncomb.id);
break;
case FF_SPRING:
pr_debug("Starting SPRING id %d\n", command->u.uncomb.id);
break;
}
break;
case LGFF_STOP_UNCOMB:
pr_debug("Stopping UNCOMBINABLE effect.\n");
switch (command->u.uncomb.effect->type) {
case FF_DAMPER:
pr_debug("Stopping DAMPER id %d\n", command->u.uncomb.id);
break;
case FF_FRICTION:
pr_debug("Stopping FRICTION id %d\n", command->u.uncomb.id);
break;
case FF_INERTIA:
pr_debug("Stopping INERTIA id %d\n", command->u.uncomb.id);
break;
case FF_SPRING:
pr_debug("Stopping SPRING id %d\n", command->u.uncomb.id);
break;
}
break;
}
return 0;
}
static void ff_dummy_close(int idx)
{
if (idx < 0 || idx > max_device_count) {
printk(KERN_WARNING "Invalid device index.\n");
return;
}
if (!ff_dummy_devices[idx])
return;
mutex_lock(&mtx);
input_unregister_device(ff_dummy_devices[idx]->dev);
kfree(ff_dummy_devices[idx]);
ff_dummy_devices[idx] = NULL;
device_count--;
mutex_unlock(&mtx);
printk(KERN_NOTICE "Dummy force feedback %u device removed.\n", idx);
}
static int ff_dummy_open(int idx)
{
struct ff_dummy_device *newdummy;
struct input_dev *newdev;
int i, ret;
if (idx < 0 || idx >= max_device_count) {
printk(KERN_WARNING "Invalid device index.\n");
return -EINVAL;
}
newdummy = kzalloc(sizeof(struct ff_dummy_device *), GFP_KERNEL);
if (!newdummy) {
printk(KERN_ERR "Unable to allocate memory for dummy device slot.\n");
return -ENOMEM;
}
mutex_lock(&mtx);
if (ff_dummy_devices[idx]) {
printk(KERN_WARNING "Selected slot %u already occupied.\n", idx);
mutex_unlock(&mtx);
kfree(newdummy);
return -EINVAL;
}
ff_dummy_devices[idx] = newdummy;
newdev = input_allocate_device();
if (!newdev) {
printk(KERN_ERR "Unable to allocate memory for input device.\n");
ret = -ENOMEM;
goto open_err;
}
newdev->id.bustype = BUS_VIRTUAL;
newdev->id.vendor = 0xffff;
newdev->id.product = 0x0001;
newdev->id.version = 0x0001;
newdev->name = ff_dummy_name;
newdev->uniq = kasprintf(GFP_KERNEL, "%s_%u", ff_dummy_name, idx);
set_bit(EV_FF, newdev->evbit);
/* Make the device look like force feedback device */
for (i = 0; ff_dummy_effects[i] >= 0; i++)
set_bit(ff_dummy_effects[i], newdev->ffbit);
ret = input_register_device(newdev);
if (ret) {
printk(KERN_ERR "Unable to register dummy device.\n");
input_free_device(newdev);
goto open_err;
}
ret = input_ff_create_logitech(newdev, (void *)ff_dummy_devices[idx], ff_dummy_control);
if (ret) {
printk(KERN_ERR "Unable to register dummy device with ff-logitech\n");
input_unregister_device(newdev);
goto open_err;
}
ff_dummy_devices[idx]->dev = newdev;
ff_dummy_devices[idx]->slot = idx;
device_count++;
mutex_unlock(&mtx);
printk(KERN_NOTICE "Dummy force feedback %u device created.\n", idx);
return 0;
open_err:
kfree(ff_dummy_devices[idx]);
ff_dummy_devices[idx] = NULL;
mutex_unlock(&mtx);
return ret;
}
static ssize_t ff_dummy_add_device_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int ret;
mutex_lock(&mtx);
ret = scnprintf(buf, PAGE_SIZE, "%u\n", device_count);
mutex_unlock(&mtx);
return ret;
}
static ssize_t ff_dummy_add_device_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
size_t count)
{
int ret;
int idx = 0;
sscanf(buf, "%d", &idx);
ret = ff_dummy_open(idx);
if (ret) {
printk(KERN_ERR "Dummy device creation failed with errno %d\n", ret);
return ret;
}
return count;
}
static ssize_t ff_dummy_del_device_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int i;
int ret = 0;
mutex_lock(&mtx);
for (i = 0; i < max_device_count; i++) {
if (ff_dummy_devices[i])
ret += scnprintf(buf + ret, PAGE_SIZE, "[%u] ", i);
else
ret += scnprintf(buf + ret, PAGE_SIZE, "%u ", i);
}
mutex_unlock(&mtx);
ret += scnprintf(buf + ret, PAGE_SIZE, "\n");
return ret;
}
static ssize_t ff_dummy_del_device_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
size_t count)
{
int idx = 0;
sscanf(buf, "%d", &idx);
ff_dummy_close(idx);
return count;
}
static struct kobj_attribute add_device_attr =
__ATTR(add_device, 0644, ff_dummy_add_device_show, ff_dummy_add_device_store);
static struct kobj_attribute del_device_attr =
__ATTR(del_device, 0644, ff_dummy_del_device_show, ff_dummy_del_device_store);
static struct attribute *attrs[] = {
&add_device_attr.attr,
&del_device_attr.attr,
NULL
};
static struct attribute_group attrs_grp = {
.attrs = attrs
};
static void __exit ff_dummy_exit(void)
{
int i;
sysfs_remove_group(ff_dummy_kobj, &attrs_grp);
for (i = 0; i < max_device_count; i++)
if (ff_dummy_devices[i])
ff_dummy_close(i);
kfree(ff_dummy_devices);
kobject_put(ff_dummy_kobj);
printk(KERN_NOTICE "Dummy force feedback module removed.\n");
}
static int __init ff_dummy_init(void)
{
int ret;
if (max_device_count < 1)
max_device_count = 2;
ff_dummy_kobj = kobject_create_and_add("ff_dummy_device_obj", kernel_kobj);
if (!ff_dummy_kobj)
return -ENOMEM;
ff_dummy_devices = kcalloc(max_device_count, sizeof(struct ff_dummy_device *), GFP_KERNEL);
if (!ff_dummy_devices) {
ret = -ENOMEM;
goto err_alloc;
}
mutex_init(&mtx);
ret = sysfs_create_group(ff_dummy_kobj, &attrs_grp);
if (ret)
goto err_sysfs;
ret = ff_dummy_open(0);
if (ret)
goto err_open;
printk(KERN_NOTICE "Dummy force feedback module loaded.\n");
return 0;
err_open:
sysfs_remove_group(ff_dummy_kobj, &attrs_grp);
err_sysfs:
kfree(ff_dummy_devices);
err_alloc:
kobject_put(ff_dummy_kobj);
return ret;
}
module_param_named(max_device_count, max_device_count, int, S_IRUGO);
MODULE_PARM_DESC(max_device_count, "Maximum number of dummy devices that can be created simultaneously.");
module_exit(ff_dummy_exit);
module_init(ff_dummy_init);
EDIT:
The scheduling problem might have been a false lead. The only thing I am certain about is that the crash happens deep inside input_register_device(). "in_atomic()" and "in_interrupt()" return 0.
EDIT:
Solved. The actual bug was caused by a double free error in the underlying ff-logitech module and incorrect allocation of "struct ff_dummy_device *" instead of "struct ff_dummy_device".