0

I am wondering what happens when two processes write to a character device file at the same time. Currently, I am mostly worried about /dev/spidev0.0 on a Raspberry pi.

If I assume correctly that it's the drivers task to deal with concurrent writes, does the driver see which processes have written which data? Or does the driver only see only continous data stream where all the concurrent writes get mixed in?

4
  • I am not sure what your question is. The driver can certainly "see" which process is writing right now, but what do you expect it to do with this information? Whenever process A, or B, or C etc. writes to the device, a character is output, except if spidev uses some locking protocol. Apr 6, 2021 at 23:39
  • So the driver reads and sends one byte from A, then one from B, then C, A and so on, until all streams are empty? Or does it empty every stream before going to the next? Is the resulting SPI stream ABCABCBBC or rather AABBBCCC?
    – Daniel D.
    Apr 6, 2021 at 23:43
  • I don't think the driver has a notion of "streams". It could wait until process A closes the device file, but I can't imagine it does that. However, according to the answer, it does write out an entire buffer without interference from other processes. The buffer has a max size of bufsiz, which is likely to be a rather small number. Apr 7, 2021 at 1:36
  • In general, I would very much expect writes to be atomic wrt. each other. If one process writes aaa and another writes bb, the result better be either aaabb, or bbaaa (for non-seekable streams). POSIX dictates that for regular files and pipes up to some maximum write length (PIPE_BUF), and I'd be surprised if the same didn't apply to stream sockets (e.g. TCP) or "regular" UART serial ports on serious systems. (For regular files, the writes can of course overlap, but they still can't interleave.) Anyway, SPI isn't exactly a stream protocol, it has to guarantee intact transfers.
    – ilkkachu
    Apr 7, 2021 at 21:05

1 Answer 1

4

Looking at the Linux 5.4.97 kernel source, I see drivers/spi/spidev.c. In that file, I see the function that handles write requests:

ssize_t
spidev_write(struct file *filp, const char __user *buf,
                size_t count, loff_t *f_pos)
{
        struct spidev_data      *spidev;
        ssize_t                 status = 0;
        unsigned long           missing;

        /* chipselect only toggles at start or end of operation */
        if (count > bufsiz)
                return -EMSGSIZE;

        spidev = filp->private_data;

        mutex_lock(&spidev->buf_lock);
        missing = copy_from_user(spidev->tx_buffer, buf, count);
        if (missing == 0)
                status = spidev_sync_write(spidev, count);
        else
                status = -EFAULT;
        mutex_unlock(&spidev->buf_lock);

        return status;
}

Note the call to mutex_lock() and mutex_unlock(). A process calling write() on the character device will acquire the mutex, write all of its data, then unlock the mutex. If there was some other writer that came along during that process, that writer would block on the call to mutex_lock() until the existing writer called mutex_unlock().

Any writer that writes is guaranteed to write the entire buffer without interleaving from other writers.

5
  • so that's the callback called when some other process writes to the /dev/spi... file? Just for my understanding: The mutex is really being locked by the spi driver, not by the writing process? Does is make any difference whether the writing processes open and close the file or just keep it opened the whole time and write to it periodically?
    – Daniel D.
    Apr 7, 2021 at 10:40
  • The writing process invokes the write() system call on an open file descriptor associated with the character device. That leads to the execution of the above function in the kernel to handle that operation. @DanielD. Apr 7, 2021 at 15:24
  • With SPI, the writes aren't as much streaming bytes (as with a "regular" serial port or a TCP socket), but sending full messages (as with an UDP socket). The driver controls the chip select / chip enable lines too, and deactivates them between all read() and write() calls (kernel.org/doc/html/latest/spi/spidev.html). SPI devices can well depend on the chip enable to identify the start and end of a transfer. (Worse, they may require both writes and reads within the same transfer.)
    – ilkkachu
    Apr 7, 2021 at 20:53
  • Thanks to both of you for taking your time and looking up the code, really helped me to understand things :)
    – Daniel D.
    Apr 8, 2021 at 10:50
  • what if the user process gets signaled (eg.SIGKILL) while in the kernel context? does the lock stop? or does the process waits until the syscall returns?
    – Alexis
    Mar 30, 2022 at 13:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .