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 disklabel
DIOCSDINFO
struct disklabel
DIOCWDINFO
struct disklabel
DIOCGPART
struct partinfo
DIOCRFORMAT
struct format_op
DIOCWFORMAT
struct format_op
DIOCSSTEP
int
DIOCSRETRIES
int
DIOCKLABEL
int
DIOCWLABEL
int
DIOCSBAD
struct dkbad
DIOCEJECT
int
DIOCLOCK
int
DIOCGDEFLABEL
struct disklabel
DIOCCLRLABEL
DIOCGCACHE
int
DKCACHE_READ
DKCACHE_WRITE
DKCACHE_RCHANGE
DKCACHE_WCHANGE
DKCACHE_SAVE
DIOCSCACHE
int
DIOCGCACHE
.DIOCCACHESYNC
int
DIOCBSLIST
struct disk_badsecinfo
DIOCBSFLUSH
DIOCAWEDGE
struct dkwedge_info
DIOCGWEDGEINFO
struct dkwedge_info
DIOCDWEDGE
struct dkwedge_info
DIOCLWEDGES
struct dkwedge_list
DIOCGSTRATEGY
struct disk_strategy
DIOCSSTRATEGY
struct disk_strategy
DIOCGDISKINFO
struct plistref
DIOCGMEDIASIZE
off_t
DIOCGSECTORSIZE
u_int
Each 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.0 |