clinuxlinux-kernellinux-device-driverprintk

copy_to_user() keeps printing message infinitely


I am learning the "Linux device drivers". I created a character device named char_device. When I read the data from the device it keeps printing the message to terminal infinitely crashing the machine.

Source code of the read operation in the driver:

static ssize_t my_read(struct file *my_file, char __user *buf, size_t len, loff_t *off) {
    uint8_t *message = "Hello from the kernel world!\n";
    size_t datalen = strlen(message);
    
    if(len > datalen) {
        len = datalen;
    }
    printk(KERN_INFO "Char driver: Read");
    if(copy_to_user(buf, message, len)) {
        return -EFAULT;
    }

    return len;
}

User space command used to read the device:

cat /dev/char_device

The driver keeps printing "Hello from the kernel world!" message to the terminal.


Solution

  • Programs such as cat will read the current input file until it encounters an end-of-file condition or a read error. By convention, a read() return value of 0 indicates an end-of-file condition when a non-zero amount of data is requested. A return value of -1 indicates a read error with the error number in the errno variable.

    At the kernel level, the read file operation handler should return 0 to indicate an end-of-file condition (but may also do so when the requested amount in 0), and should return a negated errno value to indicate a read error.

    OP's current read file operation handler, my_read, copies the contents of a string literal, "Hello from the kernel world!\n", to the user memory buffer, limited to the requested read amount or the length of the string, whichever is smallest. It never returns an end-of-file condition. The only time it returns 0 is when the requested amount is 0, but that is not a valid end-of-file condition.

    One thing that my_read could do is to use the passed in file position information to determine where to start reading, and to limit the amount to be copied. It should update the position accordingly. Then it can return 0 when to indicate end-of-file when there is no more data to be copied. Here is a possible implementation:

    static ssize_t my_read(struct file *my_file, char __user *buf, size_t len, loff_t *off) {
        char *message = "Hello from the kernel world!\n";
        size_t datalen = strlen(message);
       
        if (*off >= datalen) {
            return 0; /* end of file */
        } 
        if(len > datalen - *off) {
            len = datalen - *off;
        }
        printk(KERN_INFO "Char driver: Read\n");
        if(copy_to_user(buf, message, len)) {
            return -EFAULT;
        }
    
        *off += len;
        return len;
    }
    

    One change in behavior is that successive read amounts of length 10 (for example) will pick up from where the previous read left off, for example: