| DISK(9) | Kernel Developer's Manual | DISK(9) | 
disk, disk_init,
  disk_attach, disk_begindetach,
  disk_detach, disk_destroy,
  disk_wait, disk_busy,
  disk_unbusy, disk_isbusy,
  disk_find, disk_set_info
  —
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
void
  
  disk_init(struct
    disk *, const char
    *name, const struct
    dkdriver *driver);
void
  
  disk_attach(struct
    disk *);
void
  
  disk_begindetach(struct
    disk *, int
    (*lastclose)(device_t),
    device_t self,
    int flags);
void
  
  disk_detach(struct
    disk *);
void
  
  disk_destroy(struct
    disk *);
void
  
  disk_wait(struct
    disk *);
void
  
  disk_busy(struct
    disk *);
void
  
  disk_unbusy(struct
    disk *, long
    bcount, int
  read);
bool
  
  disk_isbusy(struct
    disk *);
struct disk *
  
  disk_find(const
    char *);
void
  
  disk_set_info(device_t,
    struct disk *,
    const char *type);
disk structure, which is defined as follows:
struct disk {
	TAILQ_ENTRY(disk) dk_link;	/* link in global disklist */
	const char	*dk_name;	/* disk name */
	prop_dictionary_t dk_info;	/* reference to disk-info dictionary */
	int		dk_bopenmask;	/* block devices open */
	int		dk_copenmask;	/* character devices open */
	int		dk_openmask;	/* composite (bopen|copen) */
	int		dk_state;	/* label state   ### */
	int		dk_blkshift;	/* shift to convert DEV_BSIZE to blks */
	int		dk_byteshift;	/* shift to convert bytes to blks */
	/*
	 * Metrics data; note that some metrics may have no meaning
	 * on certain types of disks.
	 */
	struct io_stats	*dk_stats;
	const struct dkdriver *dk_driver;	/* pointer to driver */
	/*
	 * Information required to be the parent of a disk wedge.
	 */
	kmutex_t	dk_rawlock;	/* lock on these fields */
	u_int		dk_rawopens;	/* # of opens of rawvp */
	struct vnode	*dk_rawvp;	/* vnode for the RAW_PART bdev */
	kmutex_t	dk_openlock;	/* lock on these and openmask */
	u_int		dk_nwedges;	/* # of configured wedges */
					/* all wedges on this disk */
	LIST_HEAD(, dkwedge_softc) dk_wedges;
	/*
	 * Disk label information.  Storage for the in-core disk label
	 * must be dynamically allocated, otherwise the size of this
	 * structure becomes machine-dependent.
	 */
	daddr_t		dk_labelsector;		/* sector containing label */
	struct disklabel *dk_label;	/* label */
	struct cpu_disklabel *dk_cpulabel;
};
The system maintains a global linked-list of all disks attached to
    the system. This list, called disklist, may grow or
    shrink over time as disks are dynamically added and removed from the system.
    Drivers which currently make use of the detachment capability of the
    framework are the ccd, dm,
    and vnd pseudo-device drivers.
The following is a brief description of each function in the framework:
disk_init()disk_attach()disk_begindetach()DETACH_FORCE is not set in
      flags, return EBUSY.
      Otherwise, call the provided lastclose routine (if
      not NULL) and return its exit code.disk_detach()disk_destroy()disk_wait()disk_wait() increment the disk's wait counter and
      handles the accumulation.disk_busy()disk_wait(), then only the values from the busy
      counter are available.disk_unbusy()disk_isbusy()disk_find()NULL if the disk does not exist.disk_set_info()NULL, it will be
      added to the dictionary.The functions typically called by device drivers are
    disk_init() disk_attach(),
    disk_begindetach(),
    disk_detach(),
    disk_destroy(), disk_wait(),
    disk_busy(), disk_unbusy(),
    and disk_set_info(). The function
    disk_find() is provided as a utility function.
DIOCGDINFO
    struct disklabelDIOCSDINFO
    struct disklabelDIOCWDINFO
    struct disklabelDIOCGPART
    struct partinfoDIOCRFORMAT
    struct format_opDIOCWFORMAT
    struct format_opDIOCSSTEP
    intDIOCSRETRIES
    intDIOCKLABEL
    intDIOCWLABEL
    intDIOCSBAD
    struct dkbadDIOCEJECT
    intDIOCLOCK
    intDIOCGDEFLABEL
    struct disklabelDIOCCLRLABELDIOCGCACHE
    intDKCACHE_READDKCACHE_WRITEDKCACHE_RCHANGEDKCACHE_WCHANGEDKCACHE_SAVEDIOCSCACHE
    intDIOCGCACHE.DIOCCACHESYNC
    intDIOCBSLIST
    struct disk_badsecinfoDIOCBSFLUSHDIOCAWEDGE
    struct dkwedge_infoDIOCGWEDGEINFO
    struct dkwedge_infoDIOCDWEDGE
    struct dkwedge_infoDIOCLWEDGES
    struct dkwedge_listDIOCGSTRATEGY
    struct disk_strategyDIOCSSTRATEGY
    struct disk_strategyDIOCGDISKINFO
    struct plistrefDIOCGMEDIASIZE
    off_tDIOCGSECTORSIZE
    u_intEach device in the system uses a “softc” structure which contains autoconfiguration and state information for that device. In the case of disks, the softc should also contain one instance of the disk structure, e.g.:
struct foo_softc {
	device_t	sc_dev;		/* generic device information */
	struct	disk	sc_dk;		/* generic disk information */
	[ . . . more . . . ]
};
In order for the system to gather metrics data about a disk, the
    disk must be registered with the system. The
    disk_attach() routine performs all of the functions
    currently required to register a disk with the system including allocation
    of disklabel storage space, recording of the time since boot that the disk
    was attached, and insertion into the disklist. Note that since this function
    allocates storage space for the disklabel, it must be called before the
    disklabel is read from the media or used in any other way. Before
    disk_attach() is called, a portions of the disk
    structure must be initialized with data specific to that disk. For example,
    in the “foo” disk driver, the following would be performed in
    the autoconfiguration “attach” routine:
void
fooattach(device_t parent, device_t self, void *aux)
{
	struct foo_softc *sc = device_private(self);
	[ . . . ]
	/* Initialize and attach the disk structure. */
	disk_init(&sc->sc_dk, device_xname(self), &foodkdriver);
	disk_attach(&sc->sc_dk);
	/* Read geometry and fill in pertinent parts of disklabel. */
	/* Initialize geometry values of the disk structure */
	[ . . . ]
	disk_set_info(&self>, &sc->sc_dk, type);
}
The foodkdriver above is the disk's
    “driver” switch. This switch currently includes pointers to
    several driver entry points, where only the
    d_strategy entry point is used by the disk
    framework. This switch needs to have global scope and should be initialized
    as follows:
void    (foostrategy)(struct buf *);
void    (foominphys)(struct buf *);
int     (fooopen)(dev_t, int, int, struct lwp *);
int     (fooclose)(dev_t, int, int, struct lwp *);
int     (foo_discard)(device_t, off_t, off_t);
int     (foo_diskstart)(device_t, struct buf *);
void    (foo_iosize)(device_t, int *);
int     (foo_dumpblocks)(device_t, void *, daddr_t, int);
int     (foo_lastclose)(device_t);
int     (foo_firstopen)(device_t, dev_t, int, int);
int     (foo_label)(device_t, struct disklabel *);
const struct dkdriver foodkdriver = {
	.d_open = fooopen,
	.d_close = fooclose,
	.d_strategy = foostrategy,
	.d_minphys = foominphys,
	.d_discard = foo_discard,
	.d_diskstart = foo_diskstart,	/* optional */
	.d_dumpblocks = foo_dumpblocks,	/* optional */
	.d_iosize = foo_iosize,		/* optional */
	.d_firstopen = foo_firstopen,	/* optional */
	.d_lastclose = foo_lastclose,	/* optional */
	.d_label = foo_label,		/* optional */
};
Once the disk is attached, metrics may be gathered on that disk.
    In order to gather metrics data, the driver must tell the framework when the
    disk queues, starts and stops operations. This functionality is provided by
    the disk_wait(), disk_busy()
    and disk_unbusy() routines. Because
    struct disk is part of device driver private data it
    needs to be guarded. Mutual exclusion must be done by driver
    disk_wait(), disk_busy() and
    disk_unbusy() are not thread safe. The
    disk_busy() routine should be called immediately
    before a command to the disk is sent, e.g.:
void
foostrategy(struct buf *bp)
{
	[ . . . ]
	mutex_enter(&sc->sc_dk_mtx);
	disk_wait(&sc->sc_dk);
	/* Put buffer onto drive's transfer queue */
	mutex_exit(&sc->sc_dk_mtx);
	foostart(sc);
}
void
foostart(struct foo_softc *sc)
{
	[ . . . ]
	/* Get buffer from drive's transfer queue. */
	[ . . . ]
	/* Build command to send to drive. */
	[ . . . ]
	/* Tell the disk framework we're going busy. */
	mutex_enter(&sc->sc_dk_mtx);
	disk_busy(&sc->sc_dk);
	mutex_exit(&sc->sc_dk_mtx);
	/* Send command to the drive. */
	[ . . . ]
}
The routine disk_unbusy() performs some
    consistency checks, such as ensuring that the calls to
    disk_busy() and
    disk_unbusy() are balanced. It also performs the
    final steps of the metrics calcuation. A byte count is added to the disk's
    running total, and if greater than zero, the number of transfers the disk
    has performed is incremented. The third argument read
    specifies the direction of I/O; if non-zero it means reading from the disk,
    otherwise it means writing to the disk.
void
foodone(xfer)
	struct foo_xfer *xfer;
{
	struct foo_softc = (struct foo_softc *)xfer->xf_softc;
	struct buf *bp = xfer->xf_buf;
	long nbytes;
	[ . . . ]
	/*
	 * Get number of bytes transferred.  If there is no buf
	 * associated with the xfer, we are being called at the
	 * end of a non-I/O command.
	 */
	if (bp == NULL)
		nbytes = 0;
	else
		nbytes = bp->b_bcount - bp->b_resid;
	[ . . . ]
	mutex_enter(&sc->sc_dk_mtx);
	/* Notify the disk framework that we've completed the transfer. */
	disk_unbusy(&sc->sc_dk, nbytes,
	    bp != NULL ? bp->b_flags & B_READ : 0);
	mutex_exit(&sc->sc_dk_mtx);
	[ . . . ]
}
disk_isbusy() is used to get status of
    disk device it returns true if device is currently busy and false if it is
    not. Like disk_wait(),
    disk_busy() and
    disk_unbusy() it requires explicit locking from user
    side.
The NetBSD machine-independent SCSI disk and CD-ROM drivers use the disk framework. They are located in sys/scsi/sd.c and sys/scsi/cd.c.
The NetBSD ccd,
    dm, and vnd drivers use the
    detachment capability of the framework. They are located in
    sys/dev/ccd.c,
    sys/dev/vnd.c, and
    sys/dev/dm/device-mapper.c.
| March 5, 2017 | NetBSD 9.3 |