Linux kernel module - mutex lock necessary here? - c

Say I implement dev_write within my Linux kernel module as so:
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset)
{
size_t i;
for (i = 0; i < len; ++i) {
printk(KERN_INFO "buffer[%zu] is '%c'\n", i, buffer[i]);
}
return 0;
}
What happens if two different processes attempt to write to my device simultaneously? Will things execute normally? Do I need to acquire a mutex lock when dev_open is invoked, or is that unnecessary in this situation?

Related

kernel mutex vs wait_event_interruptible_exclusive

I want to write a simple char driver that implement consuming read and write, it holds a buffer and two global pointers to the buffer for the read and write offsets. Every process should read or write exclusively and also readers have to wait until there is at least 512 bytes to read. Just for the exercise I want to do it with wait_event_interruptible_exclusive instead of mutexes. My read and write are:
static ssize_t
my_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
int nbytes;
wait_event_interruptible_exclusive(wq, atomic_read(&data_sz) >=512 && (atomic_xchg(&data_sz,-1) || 1) );
nbytes = simple_read_from_buffer(buf, lbuf, &read_pos, my_buff, my_buff_size);
*ppos = read_pos;
atomic_set(&data_sz,write_pos-read_pos);
wake_up_interruptible(&wq);
return nbytes;
}
static ssize_t
my_write(struct file *file, const char __user *buf, size_t lbuf,
loff_t *ppos)
{
int nbytes;
wait_event_interruptible_exclusive(wq, atomic_read(&data_sz) != -1 && (atomic_xchg(&data_sz,-1) || 1));
nbytes = simple_write_to_buffer(my_buff, my_buff_size, &write_pos, buf, lbuf);
*ppos = write_pos;
atomic_set(&data_sz,write_pos-read_pos);
wake_up_interruptible(&wq);
return nbytes;
}
Is it ok to do it like that ? Is it possible to replace every usage of mutex with something like that ?
Also, since my offsets for the buffer are global, the ppos parameter of the read and write is ignored by the driver , and wonder if the lines *ppos=read_pos and *ppos=write_pos are needed ?
While atomic operation is executed indivisibly from the view of all processes, combination (e.g. with && operation) of atomic operations has no longer indivisible.
In your code, two readers at the same time may find atomic_read(&data_sz) >=512 to be true, and then execute atomic_xchg(&data_sz,-1) (the second reader would execute this operation with data_sz equal to -1).
For indivisibility of condition, you may protect its evaluation with spinlock. In that case, atomic variables are not needed. Example below uses spinlock provided by the waitqueue itself:
loff_t read_pos = 0, write_pos = 0;
size_t data_sz = 0;
DECLARE_WAIT_QUEUE_HEAD(wq);
static ssize_t
my_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
int nbytes;
spin_lock(&wq.lock); // Take the lock before checking condition
// Next call will drop the lock while waiting and reacquire it on wake up.
wait_event_interruptible_exclusive_locked(&wq, data_sz >= 512);
// TODO: Interruptible wait may wakeup premature; check its return value.
data_sz = -1; // Prevent other waiters to enter read/write section
spin_unlock(&wq.lock);
nbytes = simple_read_from_buffer(buf, lbuf, &read_pos, my_buff, my_buff_size);
*ppos = read_pos;
spin_lock(&wq.lock); // Take the lock before updating data_sz
data_sz = write_pos - read_pos;
// There is no 'wake_up_interruptible_locked',
// but "normal" wakeup works with interruptible waits too.
wake_up_locked(&wq);
spin_unlock(&wq.lock);
return nbytes;
}

Linux device driver buffering strategy

Lets assume that I have an external device that is constantly pushing data into a small buffer in my driver. I'm using a wait queue where an interrupt handler wakes up a waiting user process (similar to LDD (3rd edition) - Implementing a Handler).
irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
flag = 1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
ssize_t my_read(struct file *dev, char __user *buf, size_t count, loff_t *f_pos)
{
wait_event_interruptible(wq, flag != 0);
flag = 0;
copy_to_user(usr_buf, drv_buf, count);
}
/***********************User program***********************/
while(1)
{
read(fid, buffer, size);
//do stuff with data
}
The user program calls read and it waits till the interrupt gets new data from the external device. Since the external device may push data at a faster than this code can execute, what mechanisms can I use to ensure data is not overwritten before the user program copies it? Would a ring buffer like structure work here? Its not clear how to implement it.
Thanks
Yes, a ring buffer would work.
You simply have to fill the buffer from the interrupt handler and you will read it from the my_read callback.
A really naive and really really inefficient implementation could be (untested):
static irqreturn_t irq_handler(int irq, void *dev_id)
{
struct my_dev *dev = dev_id;
buf[buf_wr] = read_device(dev);
buf_wr++;
if (buf_wr >= BUFSIZE)
buf_wr = 0;
wake_up(&wq);
return IRQ_HANDLED;
}
static ssize_t my_read(struct file *file, char __user *ubuf,
size_t sz, loff_t *ppos)
{
int n, ret;
ret = wait_event_interruptible(wq,
buf_wr != buf_rd);
if (ret)
return ret;
n = buf_wr - buf_rd;
if (n < 0)
n += BUFSIZE;
n = min(count, n);
ret = copy_to_user(ubuf, buf, n);
buf_rd += n;
if (buf_rd >= BUFSIZE)
buf_rd -= BUFSIZE;
if (ret)
return ret;
*ppos += n;
return 1;
}
You may also want to use DMA or mmap or both to get something more efficient.

Linux character device driver: How to handle signals?

I'm attempting to create a small char device driver which creates a file as /dev/foobar. Among the file operations callbacks, I have
ssize_t read(file* filp, char* buf, size_t count, loff_t* f_pos)
{
printk(KERN_INFO "device_read(%p,%s,%d)", filp, buf, count);
return 0;
}
ssize_t write(file* filp, char* buf, size_t count, loff_t* f_pos)
{
printk(KERN_INFO "device_write(%p,%s,%d)", filp, buf, count);
return 0;
}
But when I try reading from the file with cat /dev/foobar, it fills the message buffer completely and the system grinds to a halt and I must delete /var/log/ or some of its contents since my virtual drive is full.
I cannot abort the reading with Ctrl-C or something. How can I make the process abortable?

Data is not written into /proc file

I wrote a kernel module which creates a /proc file. I also wrote an user space code which writes into the file and I fetch that written data to my module using "copy_from_user" method and print in the kernel logs.
The data is written successfully on the logs but when I open the proc file in an editor then its blank.
Can anyone explain me why this is so?
The user space code is
#include<fcntl.h>
main()
{
int fd=open("/proc/MY_PROC_FILE", O_RDWR);
write(fd, "linux is awesome", 16);
return 0;
}
The module is
int open_callback(struct inode *p, struct file *q)
{
printk(KERN_ALERT "open callback\n");
return 0;
}
ssize_t write_callback(struct file *p, const char __user *buffer, size_t len, loff_t *s)
{
printk(KERN_ALERT "write callback\n");
char msg[256];
copy_from_user(msg, buffer, len);
printk("%s\n", msg);
return 0;
}
static struct proc_dir_entry *my_proc_entry;
static struct file_operations fs={
.open=open_callback,
.read=read_callback,
.write=write_callback,
.release=release_callback
};
static int start(void)
{
printk(KERN_ALERT "proc module registered\n");
my_proc_entry=proc_create(file_name, 0, NULL, &fs);
if(my_proc_entry==NULL)
{
printk(KERN_ALERT "os error\n");
return -ENOMEM;
}
return 0;
}
static void stop(void)
{
remove_proc_entry(file_name, NULL);
printk(KERN_ALERT "proc module unregistered\n");
}
module_init(start);
module_exit(stop);
MODULE_LICENSE("GPL");
Thanks in advance for any help
You did not implemented read_callback()
when you gona read that proc file then that read_callback() will be called. Here you need to write code to write back(Using copy_to_user()) what was written earlier in write_callback() callback.
First you need to store what was written by user in write_callback() in some global memory for kernel space then on only you can write that back in read_callback()
This is best example of what you want to do. http://www.makelinux.net/books/lkmpg/x810

Linux device driver, Is it possible to get the minor number using a file descriptor?

I am writing a device driver for Linux. It creates a device with 4 minor numbers. Whenever we attempt to write to the device at minor number 3, we are suppose to kill the device and currently it isn't suppose to do anything else except print it is writing to the booga device. Here is some of my current code and I can post more code if necessary:
Write method:
static ssize_t booga_write (struct file *filp, const char *buf, size_t count, loff_t *f_pose) {
printk("Attempting to write to booga device\n");
/* need to protect this with a semaphore if multiple processes
will invoke this driver to prevent a race condition */
if (down_interruptible (&booga_device_stats->sem))
return (-ERESTARTSYS);
booga_device_stats->num_bytes_written += count;
up(&booga_device_stats->sem);
return count; // pretend that count bytes were written
}
How it is tested:
static void run_write_test(char *device, int bufsize)
{
char *buf;
int src;
int out;
src = open(device, O_WRONLY);
if (src < 0) {
perror("Open for write failed:");
exit(1);
}
buf = (char *) malloc(sizeof(char)*(bufsize+1));
fprintf(stderr, "Attempting to write to booga device\n");
out = write(src, buf, bufsize);
fprintf(stderr, "Wrote %d bytes.\n", out);
free(buf);
close(src);
}
I am wondering if there is a way to get the minor number. I looked in linux/fs.h and saw that the file struct has a member called private_data but whenever I attempt to call this, it will crash my system as it is currently set to null.
Or should I not be trying to get the minor number from the struct file at all and should attempt to keep track of it when I first open the device?
You can get the minor number like so:
iminor(filp->f_path.dentry->d_inode)

Resources