Linux character device driver: How to handle signals? - c

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?

Related

Can't implement write() and read() in Character Device Driver

I'm trying to create a simple character device LKM but I've been stuck for days trying to get my read and write to work correctly. Currently when I do something like:
echo hi > /dev/simple_character_device
I am able to see I'm writing the correct amount of bytes.
But when I attempt to cat out the contents of that device it will continuously loop hi until reaching a bad address. Currently I'm trying to keep track of how many bytes I've written in a global counter. But that doesn't seem right. Any help on implementing the read and write would be appreciated.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
MODULE_LICENSE("GPL");
#define BUFFER 1024
char * buffer_data;
// Count open and closed
size_t current_count;
int my_open(struct inode *, struct file *);
int my_release(struct inode *, struct file *);
ssize_t my_read(struct file *, char __user *, size_t count, loff_t *);
ssize_t my_write(struct file *, const char __user *, size_t count, loff_t *offp);
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write
};
int reg_init(void)
{
// Allocate memory to store information
buffer_data = kmalloc(BUFFER, GFP_KERNEL); // Use Kernel Flag
register_chrdev(240, "simple_character_device", &fops);
printk(KERN_ALERT "Init Allocating Memory");
return 0;
}
void reg_exit(void)
{
// Free and unregister device and data
kfree(buffer_data);
unregister_chrdev(240, "simple_character_device");
printk(KERN_ALERT "Deregister Simple Character Device");
}
int my_open(struct inode *inode, struct file *file){
printk(KERN_ALERT "Open File Device.\n");
return 0;
}
int my_release(struct inode *inode, struct file *file){
printk(KERN_ALERT "Close File Device.\n");
return 0;
}
ssize_t my_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){
// Check if we are reading within the Buffer Size
if(BUFFER - *offp < 0){
printk(KERN_ALERT "Out of buffer range.\n");
return -EFAULT;
}
// Check if we fail to copy to user
if (copy_to_user(buff, buffer_data, current_count) != 0){
printk(KERN_ALERT "Failed to send character to user\n");
return -EFAULT;
}
(*offp) += current_count;
printk(KERN_ALERT "Read %zu bytes.\n", current_count);
return current_count;
}
ssize_t my_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp){
// We need to get data FROM the user space
// Make sure we are reading within the buffer
if (*offp >= BUFFER || BUFFER - count < *offp){
printk(KERN_ALERT "ATTEMPTING TO WRITE TO OUSIDE OF BUFFER!\n");
return EFAULT;
}
// Get the amount of bytes from the user
copy_from_user(buffer_data + *offp, buff, count);
*offp += count;
printk(KERN_ALERT "Wrote %zu to the device.\n", count);
current_count = count;
return current_count;
}
module_init(reg_init);
module_exit(reg_exit);
In my_read() you must not allow reading the buffer after current_count (after the point where initialized data ends) . If the requested count goes farther you must first trim the passed count. If the resulting count is <= 0, or if the offset is after current_count you must return 0 to indicate end of file.
Also you must return the trimmed count (which was really copied to the user).
I expect that you test with echo and cat. But if you test with your own code, by reading and writing in sequence, don't forget to reset the offset using lseek() between reading and writing.
As far as I can tell your code looks like a good start but the counts and file position management require some cleanup.

Why reading from a linux module works character by character but not for whole string

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 am unable to use echo and cat in this simple device driver module.

static ssize_t device_read (struct file* filp, char *bufStoreData, size_t bufCount, loff_t* curOffset)
{
printk(KERN_INFO"reading from the device");
ret = copy_to_user(bufStoreData,virtual_device.data,bufCount);
return ret;
}
static ssize_t device_write(struct file *filp,const char* bufSourceData,size_t bufCount, loff_t* curOffset)
{
printk(KERN_INFO"writing to device");
ret=copy_from_user(virtual_device.data,bufSourceData,bufCount);
return ret;
}
I was using echo and cat command to do the user write and read but i was not reading the data properly. Maybe i am not returning right values.Is that so?
device_read() and device_write() return value is the number of read/written bytes. copy_to_user() and copy_from_user() return 0 if all bytes were copied, otherwise the numer of bytes not copied.
Probably your operation succeed and you are returning 0, which means "0 byte copied".
You must return bufCount on success and a negative error code on fail.
ret=copy_from_user(virtual_device.data,bufSourceData,bufCount);
if (ret)
return ret;
return bufCount;

In my read function myread it is continuously printing the read data when i do cat /driver/mydriver. I need to print only once how to do that

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.

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