Multi-threading access to device drivers.

Matthew Dillon dillon at apollo.backplane.com
Sat Nov 6 19:57:40 PST 2004


:As was quoted POSIX, the update should be atomic..  so my reading of
:that is that you lock the fd, when you read the offset from the fd,
:you need to also update the offset with the number of bytes read, so
:that a racing read both doesn't drop any data, nor duplicate any data..
:
:i.e.  single file with fixed records, it should be perfectly safe to
:throw five threads looping doing a read processing of the records w/o
:fear of duplicate records, or dropped records...
:
:And if we handle it in this manner, then you can allow multiple reads
:into the driver...
:
:-- 
:  John-Mark Gurney				Voice: +1 415 225 5579

    If it's a regular file then the only requirements are for data atomicy 
    against writes, and no duplication of the data space (no missing pieces,
    no overlapping pieces).  This does not preclude the ability for N threads
    to all issue a read() on the same file descriptor simultaniously.  A
    simple seek space range lock (internal to the kernel) is all that is
    required to guarentee atomicy.  Serialization is not required.  e.g.

    read(fd, buf, bytes)
    {
	off_t offset;

	serializing_lock(fd);	/* temporarily protect fd->offset */
	offset = fd->offset;
	fd->offset += bytes;	(ignoring EOF issues for the moment)
	serializing_unlock(fd);
	range_lock_shared(fd, offset, bytes);
	[ ACTUAL READ INTO USER BUFFER ]
	range_unlock(fd, offset, bytes);
    }

    Or, alternatively, if you want I/O errors to also be atomic and the
    data fits in the cache, it would be something like:

    read(fd, buf, bytes)
    {
	off_t offset;
	int done = 0;

	while (!done) {
	    offset = fd->offset;
	    [ BRING DATA INTO THE CACHE ] (note: not locked at this point)
	    serializing_lock(fd);	/* temporarily protect fd->offset */
	    if (fd->offset == offset) {	/* check for race */
		fd->offset += bytes;
		done = 1;
	    }
	    serializing_unlock(fd);
	}
	range_lock_shared(fd, offset, bytes);
	[ ACTUAL READ INTO USER BUFFER ]
	range_unlock(fd, offset, bytes);
    }

    This pseudo code ignores overlapping atomicy issues if the user
    buffer happens to be mmap()'d from the same file, and ignores deadlock
    issues for same from a different file (A,B) <-> (B,A).  Generally,
    however, that sort of deadlock detection is really easy to implement,
    and would not interfere with the basic algorithm.

    Dataspace range locking is something we will be doing in DFly though the
    purpose is primarily to allow one process to block on I/O while another
    successfully reads or writes data on the same file via the VM cache
    without both being stalled.  It is not an SMP issue per-say, even though
    it can also be used to fix SMP based parallel access situations.  It's
    really a concurrency issue between blocked I/O and cached I/O that is
    one of the biggest reasons why our FS performance sucks compared to
    linux when reading and writing a single file (e.g. database).

					-Matt
					Matthew Dillon 
					<dillon at backplane.com>


More information about the freebsd-arch mailing list