On Locking Schemes on Linux Device Drivers

Hello fellow application developer or maintainer,

recently we (cdrkit and cdrskin developers) came accross increasing problems with reliable and safe device locking. This paper enlightens the issues behind the scenes and presents possible future solutions.

Introduction

Our original concern is the influence of even read-only operations on optical media drives (recorders) during their duty as recorders -- depending on the device model such read-only work may interrupt the process badly practically destroying the medium.

Since many programs already do act on such devices in an unsafe manner, either willingly (e.g. liblkid) or accidentally (e.g. hald, opening with O_EXCL but still clashing with cdr applications working on the competing sg driver), we see the need for reliable communication in order to ensure proper device locking where appropriate, in a way which is appropriate for the particular application. In the following document, first the currently possible mechanisms are itemized with their advantages and their problems, followed by a draft of a locking scheme which shall cope with the particular requirements.

State of the practice

There are various locking techniques used in other areas which are more or less applicable in our case.

General inter-process locking mechanisms

In general, all the mechanisms listed below are not optimally appropriate for our purpose. They lack on two places which make then not reliable when used alone:

Finally, they may be sufficient to lower the risk on inappropriate operation. Which exactly are available in the wild?

Advanced Linux-specific locking mechanisms

Applicability on CD/(HD)DVD/BD drives

As explained in the introduction, the locking is important on optical media recording due to the delicate operation mode during the recording. Ideally, no application should touch them, even reading from the media is an evil task. But how does the state of the practice look like?

Proposed general locking algorithm

The following method is proposed to create a midway between the limitations of kernel and the requirements of others, also unifying the way of dealing with the device locks.

The locking methods with additional lock files are identified as inappropriate because of inconsistent security settings on different applications, see above. Instead, we propose a two-step locking method:

  1. Open the device. For applications that operate in a delicate way (burning tools), O_EXCL shall be set. For others, it may be omited.
  2. Set or check the additional fcntl lock on the device file. It must be exclusive! Sample code (by Thomas Schmidt)

        struct flock lockthing;
        ...
        f = open(device, mode|O_EXCL);
        if (f != -1) {
             memset(&lockthing, 0, sizeof(lockthing));
             lockthing.l_type = F_WRLCK;
             lockthing.l_whence = SEEK_SET;
             lockthing.l_start = 0;
             lockthing.l_len = 0;
             if (fcntl(f, F_SETLK, &lockthing)) {
                close(f);
                /* user feedback, report error, etc... */
                f = -1;
             }
        }

Unique file resolution

The following applies only to applications using the delicate operation mode (burning).

For kernel 2.6, it is possible to retrieve physical address information from /dev/sgX which can be used to scan "/dev/sr%d" nodes quickly in this order unless the first node matching the physical address of /dev/sgX device in question is found. Thereafter this device file should be used for locking and further communication. Since no additional physical information is maintained by the kernel for /dev/hdX and maybe other types of devices, the locking shall happen on the device chosen by user. If some kind of fake SCSI hardware address emulation needs to be performed (for example for old cdrecord-using applications) then the scanning mechanism should always select the first appropriate device when scanning device files matching the "/dev/hd%c" pattern beginning "/dev/hda".

NOTE: there are sysfs symlinks that set up a path usable to map exactly. However, this depends on a mounted sysfs and the required symlinks have also been declared deprecated in the recent Linux kernel versions.

For kernel 2.4, we are almost out of luck. On the one hand, only /dev/sg* devices are recommended for burning, on the other hand there are no good ways to map the identities between /dev/{sg,sr,hd}* devices. The best thing that can be done is the use of fcntl locks (see above) applied on the device file specified by the user. When an alternative SCSI address (real or fake) is used for device specifications, the device scanning method shall happen in the way described above for kernel 2.6.x. When the old unofficial ATAPI interface is used with /dev/hdX nodes, the locking shall be performed on them directly.

Appendix

Why not lock files? As told above...

First: races

Second: unclear or unreliable cleanup technique, dangling bad lockfiles possible

Third: The most obvious problem is the usual permission setting of /var/lock :

SuSE 9.0 (kernel 2.4):
  drwxrwxr-x    3 root     uucp         4096 Apr  4     05:07 /var/lock
SuSE 9.3 (kernel 2.6):
  drwxrwxr-t    4 root     uucp         4096 2007-04-04 17:50 /var/lock
| Fedora Core 3.x:
|   drwxrwxr-x    5 root     lock         4096 Apr  4     04:03 /var/lock
| Debian gives rw-permission to anybody and thus would be no problem.

This system may work with the plain old UUCP program and few others programs with low device opening activity AND administrated by root but is a real PITA nowadays.