Question

I have written a small driver to read some data and give it to the user. My driver can be used by more than one application, i.e. it's a reentrant driver, hence the use of a spin lock. But I discovered that copy_to_user should not be called with a spin lock held. char_device_buf in the following code is shared data; I have to protect it. Is there any mechanism apart from mutex to use spin lock and use copy_to_user?

static ssize_t char_dev_read(struct file *file,
                                char *buf,
                                size_t lbuf,
                                loff_t *ppos)
    {
            int maxbytes; /* number of bytes from ppos to MAX_LENGTH */
            int bytes_to_do; /* number of bytes to read */
            int nbytes; /* number of bytes actually read */

            maxbytes = MAX_LENGTH - *ppos;

            if( maxbytes > lbuf ) bytes_to_do = lbuf;
            else bytes_to_do = maxbytes;

            if( bytes_to_do == 0 ) {
                    printk("Reached end of device\n");
                    return -ENOSPC; /* Causes read() to return EOF */
            }

       /* Tesing for accidental release */
    //              accidental_release(); 

            printk(KERN_DEBUG "READER: trying for critical region lock \n");

            spin_lock(&myspin);/*begin of critical region */

                    printk(KERN_DEBUG "READER : acquired lock: executing critical code\n");
                    nbytes = bytes_to_do -
                             copy_to_user( buf, /* to */
                                           char_device_buf + *ppos, /* from */
                                           bytes_to_do ); /* how many bytes */


            spin_unlock(&myspin); /* end of critical region */
            *ppos += nbytes;
            return nbytes;
    }
Was it helpful?

Solution

The reason why copy_{to,from}_user should not be used inside a spin lock, is that these functions can sleep. Imagine this scenario (on a uni-processor machine):

  1. Process A mmap()ed a file
  2. The process calls your driver providing an address into that mmap()ed area
  3. Your code runs, locks, then copy_to_user causes a page fault on that address - the memory is not present so that process goes to sleep until the data comes from the disk.
  4. The kernel schedules to process B, it calls your the driver the same way.
  5. Deadlock - Process A is waiting for the IO to return inside a lock but won't be scheduled because B is holding the CPU waiting for the same lock to be unlocked.

Unless there is a 100% guarantee that copy_{to,from}_user won't cause a segfault, you cannot use spin locks, but must use a sleep lock instead, such as 'mutex_lock'. Sleep locks yield control to the scheduler while spin locks do not.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top