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;
}
Related
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?
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.
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?
I have my kernel module's read function as follows. It simply reads a single character from kernel buffer and copies it into user buffer.
Once I have reached the end of kernel buffer, I return 0. This works perfectly.
static ssize_t debugfs_read(struct file *f, char __user *buf,
size_t length, loff_t *offset)
{
ssize_t rc = 0;
pr_info("%s.\n", "In read");
rc = copy_to_user(buf, &kbuffer[*offset], 1);
if (rc < 0)
return -EFAULT;
if (kbuffer[*offset] == '\0')
return 0;
*offset = *offset + 1;
return 1;
}
Now If I try to copy the whole kernel buffer in one go into the user buffer,
the module does not print anything.
Why is that? The code in this case:
static ssize_t debugfs_read(struct file *f, char __user *buf,
size_t length, loff_t *offset)
{
ssize_t rc = 0;
pr_info("%s.\n", "In read");
rc = copy_to_user(buf, &kbuffer, BUFF_LEN);
if (rc != 0)
return -EFAULT;
return 0;
}
Edit:
On using the accepted answer's solution, I realized that read is called twice by kernel. Here is strace:
read(3, "s03324135655\0", 65536) = 13
write(1, "s03324135655\0", 13) = 13
read(3, "", 65536) = 0
Why is that? Is it because we must return 0 once we are done reading from a module? The second system call has an empty string essentially stipulating that there is nothing left to read and we should return zero.
If you are given a length with a buffer, you need to use it. You wouldn't want to stomp on some other user-space memory if the buffer provided is smaller than your kernel buffer.
You need to return the number of bytes that you copied to the buffer. Your character-by-character code returned 1, but your code for copying the entire buffer always returned 0 in the success case, which tells the caller you wrote 0 bytes to the buffer.
You need to take into account the offset parameter as well.
So, rather than implementing all this logic yourself, just use simple_read_from_buffer, which does this for you:
static ssize_t debugfs_read(struct file *f, char __user *buf,
size_t length, loff_t *offset)
{
return simple_read_from_buffer(buf, length, offset, &kbuffer, BUFF_LEN);
}
I wrote h into driver by doing echo:
echo -n h /dev/mydriver
When I do cat /dev/mydriver, myread function is printing h continuously. I wanted to print once. How to do that.
static char m;
static ssize_t myread(struct file *f, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read()\n");
if (copy_to_user(buf, &m, 1) != 0)
return -EFAULT;
else
return 1;
}
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write()\n");
if (copy_from_user(&c, buf + len – 1, 1) != 0)
return -EFAULT;
else
return len;
}
If you want to use standard tools (such as cat) with your custom drivers, do not forget to set offset (*loff_t off) correctly. Your read function should look something like this:
static ssize_t myread(struct file *f, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read()\n");
/* You have just a single char in your buffer, so only 0 offset is valid */
if(*off > 0)
return 0; /* End of file */
if (copy_to_user(buf, &m, 1))
return -EFAULT;
*off++;
return 1;
}
You have to think about how you want your device to work... Will what you write to it be available to multiple processes? Or should what you write be removed once it's been read?
The latter is of course easier, and can simple be implemented by clearing the variable m in the myread function. If it's zero, then return zero from the myread function.