1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00

Remove support of LUN-based CD changers from cd(4) driver.

This code was heavily broken few months ago during CAM locking changes.
Fixing it would require almost complete rewrite.  Since there are no
known devices on market using this interface younger then ~15 years, and
they are CD, not even DVD, I don't see much reason to rewrite it.

This change does not mean those devices won't work.  They will just work
slower due to inefficient disks load/unload schedule if several LUNs
accessed same time.

Discussed with:	ken@
Silence on:	scsi@, hardware@
MFC after:	1 week
This commit is contained in:
Alexander Motin 2014-04-09 08:57:57 +00:00
parent a955cde712
commit b8a8ed5664
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=264295
2 changed files with 19 additions and 777 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 8, 2012
.Dd April 9, 2014
.Dt CD 4
.Os
.Sh NAME
@ -33,8 +33,6 @@
.Nd SCSI CD-ROM driver
.Sh SYNOPSIS
.Cd device cd
.Cd "options ""CHANGER_MIN_BUSY_SECONDS=3"""
.Cd "options ""CHANGER_MAX_BUSY_SECONDS=11"""
.Sh DESCRIPTION
The
.Nm
@ -288,65 +286,6 @@ Some work is planned to support
some of the more common `broken'
.Tn CD-ROM
drives; however, this is not yet under way.
.Sh CHANGER OPERATION
This driver has built-in support for LUN-based CD changers.
A LUN-based CD
changer is a drive that can hold two or more CDs, but only has one CD
player mechanism.
Each CD in the drive shows up as a separate logical unit
on the
.Tn SCSI
bus.
The
.Nm
driver automatically recognizes LUN-based changers, and routes commands for
changers through an internal scheduler.
The scheduler prevents changer
"thrashing", which is caused by sending commands to different LUNs in the
changer at the same time.
.Pp
The scheduler honors minimum and maximum time
quanta that the driver will spend on a particular LUN.
The minimum time
is the guaranteed minimum amount of time that the driver will spend on a
given LUN, even if there is no outstanding I/O for that LUN.
The maximum
time is the maximum amount of time the changer will spend on a LUN if there
is outstanding I/O for another LUN.
If there is no outstanding I/O for
another LUN, the driver will allow indefinite access to a given LUN.
.Pp
The minimum and maximum time quanta are configurable via kernel options and
also via sysctl and kernel tunable variables.
The kernel options are:
.Pp
.Bl -item -compact
.It
.Cd "options ""CHANGER_MIN_BUSY_SECONDS=3"""
.It
.Cd "options ""CHANGER_MAX_BUSY_SECONDS=11"""
.El
.Pp
The sysctl/kernel tunable variables are:
.Pp
.Bl -item -compact
.It
.Va kern.cam.cd.changer.min_busy_seconds
.It
.Va kern.cam.cd.changer.max_busy_seconds
.El
.Pp
It is suggested that the user try experimenting with the minimum and
maximum timeouts via the sysctl variables to arrive at the proper values
for your changer.
Once you have settled on the proper timeouts for your
changer, you can then put them in your kernel config file.
.Pp
If your system does have a LUN-based changer, you may notice that the
probe messages for the various LUNs of the changer will continue to appear
while the boot process is going on.
This is normal, and is caused by the
changer scheduling code.
.Sh SYSCTL VARIABLES
The following variables are available as both
.Xr sysctl 8
@ -397,12 +336,6 @@ Valid minimum command sizes
are 6 and 10.
Any value above 6 will be rounded to 10, and any value below
6 will be rounded to 6.
.It kern.cam.cd.changer.min_busy_seconds
.It kern.cam.cd.changer.max_busy_seconds
.Pp
Tune how long individual LUNs are 'locked' for I/O operations to
optimize changer operation.
See CHANGER OPERATION section for information on how to use these items.
.El
.Sh FILES
.Bl -tag -width /dev/cd[0-9][a-h] -compact
@ -437,21 +370,3 @@ were poorly chosen, and a number of spelling errors have survived in
the names of the
.Fn ioctl
commands.
.Pp
There is no mechanism currently to set different minimum and maximum
timeouts for different CD changers; the timeout values set by the kernel
options or the sysctl variables apply to all LUN-based CD changers in the
system.
It is possible to implement such support, but the sysctl
implementation at least would be rather inelegant, because of the current
inability of the sysctl code to handle the addition of nodes after compile
time.
Thus, it would take one dynamically sized sysctl variable and a
userland utility to get/set the timeout values.
Implementation of separate
timeouts for different CD devices in the kernel config file would likely
require modification of
.Xr config 8
to support the two timeouts when hardwiring
.Nm
devices.

View File

@ -87,8 +87,6 @@ typedef enum {
CD_Q_NONE = 0x00,
CD_Q_NO_TOUCH = 0x01,
CD_Q_BCD_TRACKS = 0x02,
CD_Q_NO_CHANGER = 0x04,
CD_Q_CHANGER = 0x08,
CD_Q_10_BYTE_ONLY = 0x10
} cd_quirks;
@ -96,8 +94,6 @@ typedef enum {
"\020" \
"\001NO_TOUCH" \
"\002BCD_TRACKS" \
"\003NO_CHANGER" \
"\004CHANGER" \
"\00510_BYTE_ONLY"
typedef enum {
@ -106,7 +102,6 @@ typedef enum {
CD_FLAG_DISC_LOCKED = 0x0004,
CD_FLAG_DISC_REMOVABLE = 0x0008,
CD_FLAG_SAW_MEDIA = 0x0010,
CD_FLAG_CHANGER = 0x0040,
CD_FLAG_ACTIVE = 0x0080,
CD_FLAG_SCHED_ON_COMP = 0x0100,
CD_FLAG_RETRY_UA = 0x0200,
@ -123,13 +118,6 @@ typedef enum {
CD_CCB_RETRY_UA = 0x10
} cd_ccb_state;
typedef enum {
CHANGER_TIMEOUT_SCHED = 0x01,
CHANGER_SHORT_TMOUT_SCHED = 0x02,
CHANGER_MANUAL_CALL = 0x04,
CHANGER_NEED_TIMEOUT = 0x08
} cd_changer_flags;
#define ccb_state ppriv_field0
#define ccb_bp ppriv_ptr1
@ -157,9 +145,6 @@ struct cd_softc {
struct cd_params params;
union ccb saved_ccb;
cd_quirks quirks;
STAILQ_ENTRY(cd_softc) changer_links;
struct cdchanger *changer;
int bufs_left;
struct cam_periph *periph;
int minimum_command_size;
int outstanding_cmds;
@ -189,13 +174,6 @@ struct cd_quirk_entry {
};
/*
* The changer quirk entries aren't strictly necessary. Basically, what
* they do is tell cdregister() up front that a device is a changer.
* Otherwise, it will figure that fact out once it sees a LUN on the device
* that is greater than 0. If it is known up front that a device is a changer,
* all I/O to the device will go through the changer scheduling routines, as
* opposed to the "normal" CD code.
*
* NOTE ON 10_BYTE_ONLY quirks: Any 10_BYTE_ONLY quirks MUST be because
* your device hangs when it gets a 10 byte command. Adding a quirk just
* to get rid of the informative diagnostic message is not acceptable. All
@ -208,18 +186,6 @@ struct cd_quirk_entry {
*/
static struct cd_quirk_entry cd_quirk_table[] =
{
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"},
/*quirks*/ CD_Q_CHANGER
},
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM*",
"*"}, /* quirks */ CD_Q_CHANGER
},
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "NAKAMICH", "MJ-*", "*"},
/* quirks */ CD_Q_CHANGER
},
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"},
/* quirks */ CD_Q_BCD_TRACKS
@ -241,15 +207,11 @@ static void cdasync(void *callback_arg, u_int32_t code,
static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS);
static void cdshorttimeout(void *arg);
static void cdschedule(struct cam_periph *periph, int priority);
static void cdrunchangerqueue(void *arg);
static void cdchangerschedule(struct cd_softc *softc);
static int cdrunccb(union ccb *ccb,
int (*error_routine)(union ccb *ccb,
u_int32_t cam_flags,
u_int32_t sense_flags),
u_int32_t cam_flags, u_int32_t sense_flags);
static union ccb *cdgetccb(struct cam_periph *periph,
u_int32_t priority);
static void cddone(struct cam_periph *periph,
union ccb *start_ccb);
static union cd_pages *cdgetpage(struct cd_mode_params *mode_params);
@ -311,22 +273,12 @@ PERIPHDRIVER_DECLARE(cd, cddriver);
#ifndef CD_DEFAULT_TIMEOUT
#define CD_DEFAULT_TIMEOUT 30000
#endif
#ifndef CHANGER_MIN_BUSY_SECONDS
#define CHANGER_MIN_BUSY_SECONDS 5
#endif
#ifndef CHANGER_MAX_BUSY_SECONDS
#define CHANGER_MAX_BUSY_SECONDS 15
#endif
static int cd_poll_period = CD_DEFAULT_POLL_PERIOD;
static int cd_retry_count = CD_DEFAULT_RETRY;
static int cd_timeout = CD_DEFAULT_TIMEOUT;
static int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS;
static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS;
static SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");
static SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0,
"CD Changer");
SYSCTL_INT(_kern_cam_cd, OID_AUTO, poll_period, CTLFLAG_RW,
&cd_poll_period, 0, "Media polling period in seconds");
TUNABLE_INT("kern.cam.cd.poll_period", &cd_poll_period);
@ -336,30 +288,6 @@ TUNABLE_INT("kern.cam.cd.retry_count", &cd_retry_count);
SYSCTL_INT(_kern_cam_cd, OID_AUTO, timeout, CTLFLAG_RW,
&cd_timeout, 0, "Timeout, in us, for read operations");
TUNABLE_INT("kern.cam.cd.timeout", &cd_timeout);
SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW,
&changer_min_busy_seconds, 0, "Minimum changer scheduling quantum");
TUNABLE_INT("kern.cam.cd.changer.min_busy_seconds", &changer_min_busy_seconds);
SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW,
&changer_max_busy_seconds, 0, "Maximum changer scheduling quantum");
TUNABLE_INT("kern.cam.cd.changer.max_busy_seconds", &changer_max_busy_seconds);
struct cdchanger {
path_id_t path_id;
target_id_t target_id;
int num_devices;
struct camq devq;
struct timeval start_time;
struct cd_softc *cur_device;
struct callout short_handle;
struct callout long_handle;
volatile cd_changer_flags flags;
STAILQ_ENTRY(cdchanger) changer_links;
STAILQ_HEAD(chdevlist, cd_softc) chluns;
};
static struct mtx changerq_mtx;
static STAILQ_HEAD(changerlist, cdchanger) changerq;
static int num_changers;
static MALLOC_DEFINE(M_SCSICD, "scsi_cd", "scsi_cd buffers");
@ -368,9 +296,6 @@ cdinit(void)
{
cam_status status;
mtx_init(&changerq_mtx, "cdchangerq", "SCSI CD Changer List", MTX_DEF);
STAILQ_INIT(&changerq);
/*
* Install a global async callback. This callback will
* receive async callbacks like "new device found".
@ -417,15 +342,6 @@ cdoninvalidate(struct cam_periph *periph)
*/
bioq_flush(&softc->bio_queue, NULL, ENXIO);
/*
* If this device is part of a changer, and it was scheduled
* to run, remove it from the run queue since we just nuked
* all of its scheduled I/O.
*/
if ((softc->flags & CD_FLAG_CHANGER)
&& (softc->pinfo.index != CAM_UNQUEUED_INDEX))
camq_remove(&softc->changer->devq, softc->pinfo.index);
disk_gone(softc->disk);
}
@ -436,73 +352,6 @@ cdcleanup(struct cam_periph *periph)
softc = (struct cd_softc *)periph->softc;
/*
* In the queued, non-active case, the device in question
* has already been removed from the changer run queue. Since this
* device is active, we need to de-activate it, and schedule
* another device to run. (if there is another one to run)
*/
if ((softc->flags & CD_FLAG_CHANGER)
&& (softc->flags & CD_FLAG_ACTIVE)) {
/*
* The purpose of the short timeout is soley to determine
* whether the current device has finished or not. Well,
* since we're removing the active device, we know that it
* is finished. So, get rid of the short timeout.
* Otherwise, if we're in the time period before the short
* timeout fires, and there are no other devices in the
* queue to run, there won't be any other device put in the
* active slot. i.e., when we call cdrunchangerqueue()
* below, it won't do anything. Then, when the short
* timeout fires, it'll look at the "current device", which
* we are free below, and possibly panic the kernel on a
* bogus pointer reference.
*
* The long timeout doesn't really matter, since we
* decrement the qfrozen_cnt to indicate that there is
* nothing in the active slot now. Therefore, there won't
* be any bogus pointer references there.
*/
if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
callout_stop(&softc->changer->short_handle);
softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
}
softc->changer->devq.qfrozen_cnt--;
softc->changer->flags |= CHANGER_MANUAL_CALL;
cdrunchangerqueue(softc->changer);
}
/*
* If we're removing the last device on the changer, go ahead and
* remove the changer device structure.
*/
if ((softc->flags & CD_FLAG_CHANGER)
&& (--softc->changer->num_devices == 0)) {
/*
* Theoretically, there shouldn't be any timeouts left, but
* I'm not completely sure that that will be the case. So,
* it won't hurt to check and see if there are any left.
*/
if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) {
callout_stop(&softc->changer->long_handle);
softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED;
}
if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
callout_stop(&softc->changer->short_handle);
softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
}
mtx_lock(&changerq_mtx);
STAILQ_REMOVE(&changerq, softc->changer, cdchanger,
changer_links);
num_changers--;
mtx_unlock(&changerq_mtx);
xpt_print(periph->path, "removing changer entry\n");
free(softc->changer, M_DEVBUF);
}
cam_periph_unlock(periph);
if ((softc->flags & CD_FLAG_SCTX_INIT) != 0
&& sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
@ -833,238 +682,17 @@ cdregister(struct cam_periph *periph, void *arg)
xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
AC_SCSI_AEN | AC_UNIT_ATTENTION, cdasync, periph, periph->path);
/*
* If the target lun is greater than 0, we most likely have a CD
* changer device. Check the quirk entries as well, though, just
* in case someone has a CD tower with one lun per drive or
* something like that. Also, if we know up front that a
* particular device is a changer, we can mark it as such starting
* with lun 0, instead of lun 1. It shouldn't be necessary to have
* a quirk entry to define something as a changer, however.
*/
if (((cgd->ccb_h.target_lun > 0)
&& ((softc->quirks & CD_Q_NO_CHANGER) == 0))
|| ((softc->quirks & CD_Q_CHANGER) != 0)) {
struct cdchanger *nchanger;
struct cam_periph *nperiph;
struct cam_path *path;
cam_status status;
int found;
/* Set the changer flag in the current device's softc */
softc->flags |= CD_FLAG_CHANGER;
/*
* Now, look around for an existing changer device with the
* same path and target ID as the current device.
*/
mtx_lock(&changerq_mtx);
for (found = 0,
nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq);
nchanger != NULL;
nchanger = STAILQ_NEXT(nchanger, changer_links)){
if ((nchanger->path_id == cgd->ccb_h.path_id)
&& (nchanger->target_id == cgd->ccb_h.target_id)) {
found = 1;
break;
}
}
mtx_unlock(&changerq_mtx);
/*
* If we found a matching entry, just add this device to
* the list of devices on this changer.
*/
if (found == 1) {
struct chdevlist *chlunhead;
chlunhead = &nchanger->chluns;
/*
* XXX KDM look at consolidating this code with the
* code below in a separate function.
*/
/*
* Create a path with lun id 0, and see if we can
* find a matching device
*/
status = xpt_create_path(&path, /*periph*/ periph,
cgd->ccb_h.path_id,
cgd->ccb_h.target_id, 0);
if ((status == CAM_REQ_CMP)
&& ((nperiph = cam_periph_find(path, "cd")) != NULL)){
struct cd_softc *nsoftc;
nsoftc = (struct cd_softc *)nperiph->softc;
if ((nsoftc->flags & CD_FLAG_CHANGER) == 0){
nsoftc->flags |= CD_FLAG_CHANGER;
nchanger->num_devices++;
if (camq_resize(&nchanger->devq,
nchanger->num_devices)!=CAM_REQ_CMP){
printf("cdregister: "
"camq_resize "
"failed, changer "
"support may "
"be messed up\n");
}
nsoftc->changer = nchanger;
nsoftc->pinfo.index =CAM_UNQUEUED_INDEX;
STAILQ_INSERT_TAIL(&nchanger->chluns,
nsoftc,changer_links);
}
xpt_free_path(path);
} else if (status == CAM_REQ_CMP)
xpt_free_path(path);
else {
printf("cdregister: unable to allocate path\n"
"cdregister: changer support may be "
"broken\n");
}
nchanger->num_devices++;
softc->changer = nchanger;
softc->pinfo.index = CAM_UNQUEUED_INDEX;
if (camq_resize(&nchanger->devq,
nchanger->num_devices) != CAM_REQ_CMP) {
printf("cdregister: camq_resize "
"failed, changer support may "
"be messed up\n");
}
STAILQ_INSERT_TAIL(chlunhead, softc, changer_links);
}
/*
* In this case, we don't already have an entry for this
* particular changer, so we need to create one, add it to
* the queue, and queue this device on the list for this
* changer. Before we queue this device, however, we need
* to search for lun id 0 on this target, and add it to the
* queue first, if it exists. (and if it hasn't already
* been marked as part of the changer.)
*/
else {
nchanger = malloc(sizeof(struct cdchanger),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (nchanger == NULL) {
softc->flags &= ~CD_FLAG_CHANGER;
printf("cdregister: unable to malloc "
"changer structure\ncdregister: "
"changer support disabled\n");
/*
* Yes, gotos can be gross but in this case
* I think it's justified..
*/
goto cdregisterexit;
}
if (camq_init(&nchanger->devq, 1) != 0) {
softc->flags &= ~CD_FLAG_CHANGER;
printf("cdregister: changer support "
"disabled\n");
goto cdregisterexit;
}
nchanger->path_id = cgd->ccb_h.path_id;
nchanger->target_id = cgd->ccb_h.target_id;
/* this is superfluous, but it makes things clearer */
nchanger->num_devices = 0;
STAILQ_INIT(&nchanger->chluns);
callout_init_mtx(&nchanger->long_handle,
cam_periph_mtx(periph), 0);
callout_init_mtx(&nchanger->short_handle,
cam_periph_mtx(periph), 0);
mtx_lock(&changerq_mtx);
num_changers++;
STAILQ_INSERT_TAIL(&changerq, nchanger,
changer_links);
mtx_unlock(&changerq_mtx);
/*
* Create a path with lun id 0, and see if we can
* find a matching device
*/
status = xpt_create_path(&path, /*periph*/ periph,
cgd->ccb_h.path_id,
cgd->ccb_h.target_id, 0);
/*
* If we were able to allocate the path, and if we
* find a matching device and it isn't already
* marked as part of a changer, then we add it to
* the current changer.
*/
if ((status == CAM_REQ_CMP)
&& ((nperiph = cam_periph_find(path, "cd")) != NULL)
&& ((((struct cd_softc *)periph->softc)->flags &
CD_FLAG_CHANGER) == 0)) {
struct cd_softc *nsoftc;
nsoftc = (struct cd_softc *)nperiph->softc;
nsoftc->flags |= CD_FLAG_CHANGER;
nchanger->num_devices++;
if (camq_resize(&nchanger->devq,
nchanger->num_devices) != CAM_REQ_CMP) {
printf("cdregister: camq_resize "
"failed, changer support may "
"be messed up\n");
}
nsoftc->changer = nchanger;
nsoftc->pinfo.index = CAM_UNQUEUED_INDEX;
STAILQ_INSERT_TAIL(&nchanger->chluns,
nsoftc, changer_links);
xpt_free_path(path);
} else if (status == CAM_REQ_CMP)
xpt_free_path(path);
else {
printf("cdregister: unable to allocate path\n"
"cdregister: changer support may be "
"broken\n");
}
softc->changer = nchanger;
softc->pinfo.index = CAM_UNQUEUED_INDEX;
nchanger->num_devices++;
if (camq_resize(&nchanger->devq,
nchanger->num_devices) != CAM_REQ_CMP) {
printf("cdregister: camq_resize "
"failed, changer support may "
"be messed up\n");
}
STAILQ_INSERT_TAIL(&nchanger->chluns, softc,
changer_links);
}
}
/*
* Schedule a periodic media polling events.
*/
callout_init_mtx(&softc->mediapoll_c, cam_periph_mtx(periph), 0);
if ((softc->flags & CD_FLAG_DISC_REMOVABLE) &&
(softc->flags & CD_FLAG_CHANGER) == 0 &&
(cgd->inq_flags & SID_AEN) == 0 &&
cd_poll_period != 0)
callout_reset(&softc->mediapoll_c, cd_poll_period * hz,
cdmediapoll, periph);
cdregisterexit:
if ((softc->flags & CD_FLAG_CHANGER) == 0)
xpt_schedule(periph, CAM_PRIORITY_DEV);
else
cdschedule(periph, CAM_PRIORITY_DEV);
xpt_schedule(periph, CAM_PRIORITY_DEV);
return(CAM_REQ_CMP);
}
@ -1153,251 +781,6 @@ cdclose(struct disk *dp)
return (0);
}
static void
cdshorttimeout(void *arg)
{
struct cdchanger *changer;
changer = (struct cdchanger *)arg;
/* Always clear the short timeout flag, since that's what we're in */
changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
/*
* Check to see if there is any more pending or outstanding I/O for
* this device. If not, move it out of the active slot.
*/
if ((bioq_first(&changer->cur_device->bio_queue) == NULL)
&& (changer->cur_device->outstanding_cmds == 0)) {
changer->flags |= CHANGER_MANUAL_CALL;
cdrunchangerqueue(changer);
}
}
/*
* This is a wrapper for xpt_schedule. It only applies to changers.
*/
static void
cdschedule(struct cam_periph *periph, int priority)
{
struct cd_softc *softc;
softc = (struct cd_softc *)periph->softc;
/*
* If this device isn't currently queued, and if it isn't
* the active device, then we queue this device and run the
* changer queue if there is no timeout scheduled to do it.
* If this device is the active device, just schedule it
* to run again. If this device is queued, there should be
* a timeout in place already that will make sure it runs.
*/
if ((softc->pinfo.index == CAM_UNQUEUED_INDEX)
&& ((softc->flags & CD_FLAG_ACTIVE) == 0)) {
/*
* We don't do anything with the priority here.
* This is strictly a fifo queue.
*/
softc->pinfo.priority = CAM_PRIORITY_NORMAL;
softc->pinfo.generation = ++softc->changer->devq.generation;
camq_insert(&softc->changer->devq, (cam_pinfo *)softc);
/*
* Since we just put a device in the changer queue,
* check and see if there is a timeout scheduled for
* this changer. If so, let the timeout handle
* switching this device into the active slot. If
* not, manually call the timeout routine to
* bootstrap things.
*/
if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
&& ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
&& ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)){
softc->changer->flags |= CHANGER_MANUAL_CALL;
cdrunchangerqueue(softc->changer);
}
} else if ((softc->flags & CD_FLAG_ACTIVE)
&& ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0))
xpt_schedule(periph, priority);
}
static void
cdrunchangerqueue(void *arg)
{
struct cd_softc *softc;
struct cdchanger *changer;
int called_from_timeout;
changer = (struct cdchanger *)arg;
/*
* If we have NOT been called from cdstrategy() or cddone(), and
* instead from a timeout routine, go ahead and clear the
* timeout flag.
*/
if ((changer->flags & CHANGER_MANUAL_CALL) == 0) {
changer->flags &= ~CHANGER_TIMEOUT_SCHED;
called_from_timeout = 1;
} else
called_from_timeout = 0;
/* Always clear the manual call flag */
changer->flags &= ~CHANGER_MANUAL_CALL;
/* nothing to do if the queue is empty */
if (changer->devq.entries <= 0) {
return;
}
/*
* If the changer queue is frozen, that means we have an active
* device.
*/
if (changer->devq.qfrozen_cnt > 0) {
/*
* We always need to reset the frozen count and clear the
* active flag.
*/
changer->devq.qfrozen_cnt--;
changer->cur_device->flags &= ~CD_FLAG_ACTIVE;
changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP;
if (changer->cur_device->outstanding_cmds > 0) {
changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP;
changer->cur_device->bufs_left =
changer->cur_device->outstanding_cmds;
if (called_from_timeout) {
callout_reset(&changer->long_handle,
changer_max_busy_seconds * hz,
cdrunchangerqueue, changer);
changer->flags |= CHANGER_TIMEOUT_SCHED;
}
return;
}
/*
* Check to see whether the current device has any I/O left
* to do. If so, requeue it at the end of the queue. If
* not, there is no need to requeue it.
*/
if (bioq_first(&changer->cur_device->bio_queue) != NULL) {
changer->cur_device->pinfo.generation =
++changer->devq.generation;
camq_insert(&changer->devq,
(cam_pinfo *)changer->cur_device);
}
}
softc = (struct cd_softc *)camq_remove(&changer->devq, CAMQ_HEAD);
changer->cur_device = softc;
changer->devq.qfrozen_cnt++;
softc->flags |= CD_FLAG_ACTIVE;
/* Just in case this device is waiting */
wakeup(&softc->changer);
xpt_schedule(softc->periph, CAM_PRIORITY_NORMAL);
/*
* Get rid of any pending timeouts, and set a flag to schedule new
* ones so this device gets its full time quantum.
*/
if (changer->flags & CHANGER_TIMEOUT_SCHED) {
callout_stop(&changer->long_handle);
changer->flags &= ~CHANGER_TIMEOUT_SCHED;
}
if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
callout_stop(&changer->short_handle);
changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
}
/*
* We need to schedule timeouts, but we only do this after the
* first transaction has completed. This eliminates the changer
* switch time.
*/
changer->flags |= CHANGER_NEED_TIMEOUT;
}
static void
cdchangerschedule(struct cd_softc *softc)
{
struct cdchanger *changer;
changer = softc->changer;
/*
* If this is a changer, and this is the current device,
* and this device has at least the minimum time quantum to
* run, see if we can switch it out.
*/
if ((softc->flags & CD_FLAG_ACTIVE)
&& ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0)
&& ((changer->flags & CHANGER_NEED_TIMEOUT) == 0)) {
/*
* We try three things here. The first is that we
* check to see whether the schedule on completion
* flag is set. If it is, we decrement the number
* of buffers left, and if it's zero, we reschedule.
* Next, we check to see whether the pending buffer
* queue is empty and whether there are no
* outstanding transactions. If so, we reschedule.
* Next, we see if the pending buffer queue is empty.
* If it is, we set the number of buffers left to
* the current active buffer count and set the
* schedule on complete flag.
*/
if (softc->flags & CD_FLAG_SCHED_ON_COMP) {
if (--softc->bufs_left == 0) {
softc->changer->flags |=
CHANGER_MANUAL_CALL;
softc->flags &= ~CD_FLAG_SCHED_ON_COMP;
cdrunchangerqueue(softc->changer);
}
} else if ((bioq_first(&softc->bio_queue) == NULL)
&& (softc->outstanding_cmds == 0)) {
softc->changer->flags |= CHANGER_MANUAL_CALL;
cdrunchangerqueue(softc->changer);
}
} else if ((softc->changer->flags & CHANGER_NEED_TIMEOUT)
&& (softc->flags & CD_FLAG_ACTIVE)) {
/*
* Now that the first transaction to this
* particular device has completed, we can go ahead
* and schedule our timeouts.
*/
if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) {
callout_reset(&changer->long_handle,
changer_max_busy_seconds * hz,
cdrunchangerqueue, changer);
changer->flags |= CHANGER_TIMEOUT_SCHED;
} else
printf("cdchangerschedule: already have a long"
" timeout!\n");
if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) {
callout_reset(&changer->short_handle,
changer_min_busy_seconds * hz,
cdshorttimeout, changer);
changer->flags |= CHANGER_SHORT_TMOUT_SCHED;
} else
printf("cdchangerschedule: already have a short "
"timeout!\n");
/*
* We just scheduled timeouts, no need to schedule
* more.
*/
changer->flags &= ~CHANGER_NEED_TIMEOUT;
}
}
static int
cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb,
u_int32_t cam_flags,
@ -1414,50 +797,9 @@ cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb,
error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags,
softc->disk->d_devstat);
if (softc->flags & CD_FLAG_CHANGER)
cdchangerschedule(softc);
return(error);
}
static union ccb *
cdgetccb(struct cam_periph *periph, u_int32_t priority)
{
struct cd_softc *softc;
softc = (struct cd_softc *)periph->softc;
if (softc->flags & CD_FLAG_CHANGER) {
/*
* This should work the first time this device is woken up,
* but just in case it doesn't, we use a while loop.
*/
while ((softc->flags & CD_FLAG_ACTIVE) == 0) {
/*
* If this changer isn't already queued, queue it up.
*/
if (softc->pinfo.index == CAM_UNQUEUED_INDEX) {
softc->pinfo.priority = CAM_PRIORITY_NORMAL;
softc->pinfo.generation =
++softc->changer->devq.generation;
camq_insert(&softc->changer->devq,
(cam_pinfo *)softc);
}
if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
&& ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
&& ((softc->changer->flags
& CHANGER_SHORT_TMOUT_SCHED)==0)) {
softc->changer->flags |= CHANGER_MANUAL_CALL;
cdrunchangerqueue(softc->changer);
} else
cam_periph_sleep(periph, &softc->changer,
PRIBIO, "cgticb", 0);
}
}
return(cam_periph_getccb(periph, priority));
}
/*
* Actually translate the requested transfer into one the physical driver
* can understand. The transfer is described by a buf and will include
@ -1505,14 +847,7 @@ cdstrategy(struct bio *bp)
*/
bioq_disksort(&softc->bio_queue, bp);
/*
* Schedule ourselves for performing the work. We do things
* differently for changers.
*/
if ((softc->flags & CD_FLAG_CHANGER) == 0)
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
else
cdschedule(periph, CAM_PRIORITY_NORMAL);
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
cam_periph_unlock(periph);
return;
@ -1699,9 +1034,6 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
softc->outstanding_cmds--;
if (softc->flags & CD_FLAG_CHANGER)
cdchangerschedule(softc);
biofinish(bp, NULL, 0);
break;
}
@ -1864,8 +1196,6 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
xpt_announce_periph(periph, announce_buf);
xpt_announce_quirks(periph, softc->quirks,
CD_Q_BIT_STRING);
if (softc->flags & CD_FLAG_CHANGER)
cdchangerschedule(softc);
/*
* Create our sysctl variables, now that we know
* we have successfully attached.
@ -2841,7 +2171,7 @@ cdprevent(struct cam_periph *periph, int action)
return;
}
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_prevent(&ccb->csio,
/*retries*/ cd_retry_count,
@ -3021,7 +2351,7 @@ cdsize(struct cam_periph *periph, u_int32_t *size)
softc = (struct cd_softc *)periph->softc;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
/* XXX Should be M_WAITOK */
rcap_buf = malloc(sizeof(struct scsi_read_capacity_data),
@ -3266,9 +2596,6 @@ cdmediapoll(void *arg)
struct cam_periph *periph = arg;
struct cd_softc *softc = periph->softc;
if (softc->flags & CD_FLAG_CHANGER)
return;
if (softc->state == CD_STATE_NORMAL && !softc->tur &&
softc->outstanding_cmds == 0) {
if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
@ -3297,7 +2624,7 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
ntoc = len;
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3344,7 +2671,7 @@ cdreadsubchannel(struct cam_periph *periph, u_int32_t mode,
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3396,7 +2723,7 @@ cdgetmode(struct cam_periph *periph, struct cd_mode_params *data,
softc = (struct cd_softc *)periph->softc;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3495,7 +2822,7 @@ cdsetmode(struct cam_periph *periph, struct cd_mode_params *data)
softc = (struct cd_softc *)periph->softc;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3587,7 +2914,7 @@ cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len)
u_int8_t cdb_len;
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
/*
* Use the smallest possible command to perform the operation.
@ -3644,7 +2971,7 @@ cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts,
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3690,7 +3017,7 @@ cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex,
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3732,7 +3059,7 @@ cdpause(struct cam_periph *periph, u_int32_t go)
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
@ -3769,7 +3096,7 @@ cdstartunit(struct cam_periph *periph, int load)
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_start_stop(&ccb->csio,
/* retries */ cd_retry_count,
@ -3797,7 +3124,7 @@ cdstopunit(struct cam_periph *periph, u_int32_t eject)
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_start_stop(&ccb->csio,
/* retries */ cd_retry_count,
@ -3826,7 +3153,7 @@ cdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed)
int error;
error = 0;
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
csio = &ccb->csio;
/* Preserve old behavior: units in multiples of CDROM speed */
@ -3908,7 +3235,7 @@ cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo)
databuf = NULL;
cam_periph_lock(periph);
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_report_key(&ccb->csio,
/* retries */ cd_retry_count,
@ -4086,7 +3413,7 @@ cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo)
}
cam_periph_lock(periph);
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_send_key(&ccb->csio,
/* retries */ cd_retry_count,
@ -4190,7 +3517,7 @@ cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct)
databuf = NULL;
cam_periph_lock(periph);
ccb = cdgetccb(periph, CAM_PRIORITY_NORMAL);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
scsi_read_dvd_structure(&ccb->csio,
/* retries */ cd_retry_count,