Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
  Previous   Contents   Next 
   
 
Chapter 11

Drivers for Block Devices

This chapter describes the structure of block device drivers. The kernel views a block device as a set of randomly accessible logical blocks. The file system buffers the data blocks between a block device and the user space using a list of buf(9S) structures. Only block devices can support a file system.

This chapter provides information on the following subjects:

Block Driver Structure Overview

Figure 11-1 shows data structures and routines that define the structure of a block device driver. Device drivers typically include the following:

  • Device-loadable driver section

  • Device configuration section

  • Device access section

The shaded device access section in Figure 11-1 illustrates block driver entry points.

Figure 11-1 Block Driver Roadmap

Associated with each device driver is a dev_ops(9S) structure, which in turn refers to a cb_ops(9S) structure. See Chapter 5, Driver Autoconfiguration, for details regarding driver data structures.


Note - Some of the entry points can be replaced by nodev(9F) or nulldev(9F) as appropriate.


File I/O

A file system is a tree-structured hierarchy of directories and files. Some file systems, such as the UNIX File System (UFS), reside on block-oriented devices. File systems are created by format(1M) and newfs(1M).

When an application issues a read(2) or write(2) system call to an ordinary file on the UFS file system, the file system can call the device driver strategy(9E) entry point for the block device on which the file system resides. The file system code can call strategy(9E) several times for a single read(2) or write(2) system call.

The file system code determines the logical device address, or logical block number, for each ordinary file block and builds a block I/O request in the form of a buf(9S) structure directed at the block device. The driver strategy(9E) entry point then interprets the buf(9S) structure and completes the request.

Block Device Autoconfiguration

attach(9E) should perform the common initialization tasks for each instance of a device. Typically, these tasks include:

  • Allocating per-instance state structures

  • Mapping the device's registers

  • Registering device interrupts

  • Initializing mutex and condition variables

  • Creating power manageable components

  • Creating minor nodes

Block device drivers create minor nodes of type S_IFBLK. This causes a block special file representing the node to eventually appear in the /devices hierarchy.

Logical device names for block devices appear in the /dev/dsk directory, and consist of a controller number, bus-address number, disk number, and slice number. These names are created by the devfsadm(1M) program if the node type is set to DDI_NT_BLOCK or DDI_NT_BLOCK_CHAN. DDI_NT_BLOCK_CHAN should be specified if the device communicates on a channel (a bus with an additional level of addressability), such as SCSI disks, and causes a bus-address field (tN) to appear in the logical name. DDI_NT_BLOCK should be used for most other devices.

For each minor device (which corresponds to each partition on the disk), the driver must also create an nblocks or Nblocks property. This is an integer property giving the number of blocks supported by the minor device expressed in units of DEV_BSIZE (512 bytes). The file system uses the nblocks and Nblocks properties to determine device limits; Nblocks is the 64-bit version of nblocks and should be used with storage devices with over 1 Tbyte of storage per disk.). See "Device Properties" for more information.

Example 11-1 shows a typical attach(9E) entry point with emphasis on creating the device's minor node and the Nblocks property. Note that because this example uses Nblocks and not nblocks, it calls ddi_prop_update_int64(9F) instead of ddi_prop_update_int(9F).

As a side note, this example shows the use of makedevice(9F) to create a device number for ddi_prop_update_int64(9F). makedevice(9F) itself makes use of ddi_driver_major(9F), which generates a major number from a pointer to a dev_info_t structure, just as getmajor(9F) does with a dev_t structure pointer.


Example 11-1 Block Driver attach(9E) Routine

static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
     int instance = ddi_get_instance(dip);
     switch (cmd) {
       case DDI_ATTACH:
              allocate a state structure and initialize it
              map the devices registers
              add the device driver's interrupt handler(s)
              initialize any mutexes and condition variables
              read label information if the device is a disk
              create power manageable components
           /*
            * Create the device minor node. Note that the node_type
            * argument is set to DDI_NT_BLOCK.
            */
           if (ddi_create_minor_node(dip, "minor_name", S_IFBLK,
                   instance,  DDI_NT_BLOCK, 0) == DDI_FAILURE) {
                      free resources allocated so far
                  /* Remove any previously allocated minor nodes */
                  ddi_remove_minor_node(dip, NULL);
                  return (DDI_FAILURE);
            }
           /*
            * Create driver properties like "Nblocks". If the device
            * is a disk, the Nblocks property is usually calculated from
            * information in the disk label.  Use "Nblocks" instead of
            * "nblocks" to ensure the property works for large disks.
            */
            xsp->Nblocks = size of device in 512 byte blocks;
            maj_number = ddi_driver_major(dip);
           if (ddi_prop_update_int64(makedevice(maj_number, instance), dip, 
                  "Nblocks", xsp->Nblocks) != DDI_PROP_SUCCESS) {
                  cmn_err(CE_CONT, "%s: cannot create Nblocks property\n",
                           ddi_get_name(dip));
                 free resources allocated so far
                 return (DDI_FAILURE);
           }
           xsp->open = 0;
           xsp->nlayered = 0;
           ...
           return (DDI_SUCCESS);

        case DDI_RESUME:
            For information, see Chapter 9, Power Management
       default:
              return (DDI_FAILURE);
     }
}

 
 
 
  Previous   Contents   Next