Abwärtskompatibilität

In der Schicht der Block-Geräte hat sich viel getan, das meiste davon zwischen den stabilen Versionen 2.2 und 2.4. Hier folgt eine kurze Zusammenfassung der Unterschiede. Wie immer können Sie sich die Treiber in dem Beispiel-Code, der unter 2.0, 2.2 und 2.4 funktioniert, anschauen, um zu sehen, wie die Portabilitätsprobleme angegangen wurden.

Die Struktur block_device_operations gab es in Linux 2.2 noch nicht. Statt dessen verwendeten Block-Treiber eine file_operations-Struktur genau wie Zeichen-Treiber. Die Methoden check_media_change und revalidate waren ein Teil dieser Struktur. Der Kernel stellte außerdem einen Satz generischer Funktionen — block_read, block_write und block_fsync — zur Verfügung, den die meisten Treiber in ihren file_operations-Strukturen verwendeten. Eine typische 2.2- oder 2.0-Initialisierung von file_operations sah so aus:


struct file_operations sbull_bdops = {
    read:       block_read,
    write:      block_write,
    ioctl:      sbull_ioctl,
    open:       sbull_open,
    release:    sbull_release,
    fsync:      block_fsync,
    check_media_change: sbull_check_change,
    revalidate: sbull_revalidate
};

Beachten Sie, daß Block-Treiber bezüglich den Prototypen aus file_operations den gleichen Änderungen von Version 2.0 zu 2.2 unterliegen wie Zeichen-Treiber.

In 2.2 und davor wurde die request-Funktion im globalen Array blk_dev gespeichert. Zur Initialisierung war folgende Zeile erforderlich:


blk_dev[major].request_fn = sbull_request;

Weil diese Methode nur eine Warteschlange per Major-Nummer erlaubt, unterstützen Kernel vor 2.4 keine mehrfachen Warteschlangen. Da es nur eine Warteschlange gab, brauchte die request-Funktion die Warteschlange nicht als Argument, deswegen war es auch nicht vorhanden. Der Prototyp sah folgendermaßen aus:


void (*request) (void);

Alle Warteschlangen hatten außerdem aktive Köpfe, blk_queue_headactive gab es deswegen nicht.

Es gibt in 2.2 und davor keine Funktion blk_ioctl. Allerdings gab es ein Makro namens RO_IOCTLS, das in einer switch-Anweisung eingesetzt werden konnte, um BLKROSET und BLKROGET zu implementieren. sysdep.h in den Beispiel-Quellen enthält eine Implementation von blk_ioctl, die RO_IOCTLS verwendet und auch einige andere Standard-ioctl-Befehle implementiert:


#ifdef RO_IOCTLS
static inline int blk_ioctl(kdev_t dev, unsigned int cmd,
                            unsigned long arg)
{
    int err;

    switch (cmd) {
      case BLKRAGET: /* den Vorauslesewert zurueckgeben */
        if (!arg)  return -EINVAL;
        err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
        if (err) return -EFAULT;
        PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);
        return 0;

      case BLKRASET: /* den Vorauslesewert setzen */
        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
        if (arg > 0xff) return -EINVAL; /* limit it */
        read_ahead[MAJOR(dev)] = arg;
        return 0;

      case BLKFLSBUF: /* herausschreiben */
        if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* nur root */
        fsync_dev(dev);
        invalidate_buffers(dev);
        return 0;

        RO_IOCTLS(dev, arg);
    }
    return -ENOTTY;
}
#endif  /* RO_IOCTLS */

Die Befehle BLKFRAGET, BLKFRASET, BLKSECTGET, BLKSECTSET, BLKELVGET und BLKELVSET kamen mit Linux 2.2 hinzu, BLKPG mit Linux 2.4.

Linux 2.0 kannte das Array max_readahead nicht. Das Array max_segments gab es dagegen schon, und es wurde auch in Linux 2.0 und 2.2 verwendet; Gerätetreiber mußten es aber normalerweise nicht füllen.

Schließlich gibt es register_disk erst seit Linux 2.4. Statt dessen gab es eine Funktion namens resetup_one_dev, die eine ähnliche Aufgabe erfüllte:


resetup_one_dev(struct gendisk *gd, int drive);

register_disk wird in sysdep.h mit folgendem Code implementiert:


static inline void register_disk(struct gendisk *gdev, kdev_t dev,
                unsigned minors, struct file_operations *ops, long size)
{
    if (! gdev)
        return;
    resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);
}

Linux 2.0 unterschied sich natürlich auch insofern, als daß es keine feine SMP-Fähigkeit gab. Daher gab es auch kein io_request_lock und sehr viel weniger Probleme mit dem nebenläufigen Zugriff auf die I/O-Anfrage-Warteschlange.

> > Noch ein letzte wissenswerte Information: Obwohl niemand wirklich weiß, was in der 2.5-Entwicklungsserie passieren wird, ist es doch ziemlich sicher, daß die Block-Geräte noch einmal massiv überarbeitet werden. Viele Leute sind mit dem Design dieser Schicht unzufrieden, und es gibt einen hohen Druck, sie neu zu implementieren.