Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a spin lock if copy_to_user needs to be called?

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;
    }
like image 770
Suri Avatar asked Sep 04 '12 08:09

Suri


People also ask

How do you implement a spin lock?

A spinlock implementation requires a special kind of instruction known as a read-modify-write (RMW) instruction. These expensive operations are useful because they act atomically preventing a data race in multithreaded kernels.

Why are spin locks normally avoided?

However, spinlocks become wasteful if held for longer durations, as they may prevent other threads from running and require rescheduling. The longer a thread holds a lock, the greater the risk that the thread will be interrupted by the OS scheduler while holding the lock.

What is the use of spin lock?

A SpinLock is an alternative to blocking synchronization. SpinLock (also known as "Busy Waiting") is a mechanism that can be used to make a thread trying to acquire a lock wait in a loop till it can get access to the resource. Note that SpinLock can perform faster compared to Mutex since context switching is reduced.

How do you unlock a SpinLock?

Use the pthread_spin_unlock(3C) function to release a locked spin lock.


1 Answers

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.

like image 161
Dan Aloni Avatar answered Nov 03 '22 21:11

Dan Aloni