Make ada(4) driver to control device write cache, same as ata(4) does.
Add kern.cam.ada.write_cache sysctl/tunable to control it alike hw.ata.wc.
This commit is contained in:
parent
bd0d62016a
commit
f513d14ca4
@ -25,7 +25,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd October 24, 2010
|
.Dd April 7, 2011
|
||||||
.Dt ADA 4
|
.Dt ADA 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -123,6 +123,11 @@ seconds.
|
|||||||
This variable determines whether to spin-down disks when shutting down.
|
This variable determines whether to spin-down disks when shutting down.
|
||||||
Set to 1 to enable spin-down, 0 to disable.
|
Set to 1 to enable spin-down, 0 to disable.
|
||||||
The default is currently enabled.
|
The default is currently enabled.
|
||||||
|
.It kern.cam.ada.write_cache
|
||||||
|
.Pp
|
||||||
|
This variable determines whether device write cache should be enabled or not.
|
||||||
|
Set to 1 to enable write cache, 0 to disable, -1 to left it as-is.
|
||||||
|
The default is currently enabled.
|
||||||
.El
|
.El
|
||||||
.Sh FILES
|
.Sh FILES
|
||||||
.Bl -tag -width ".Pa /dev/ada*" -compact
|
.Bl -tag -width ".Pa /dev/ada*" -compact
|
||||||
|
@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#define ATA_MAX_28BIT_LBA 268435455UL
|
#define ATA_MAX_28BIT_LBA 268435455UL
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
ADA_STATE_WCACHE,
|
||||||
ADA_STATE_NORMAL
|
ADA_STATE_NORMAL
|
||||||
} ada_state;
|
} ada_state;
|
||||||
|
|
||||||
@ -89,6 +90,7 @@ typedef enum {
|
|||||||
} ada_quirks;
|
} ada_quirks;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
ADA_CCB_WCACHE = 0x01,
|
||||||
ADA_CCB_BUFFER_IO = 0x03,
|
ADA_CCB_BUFFER_IO = 0x03,
|
||||||
ADA_CCB_WAITING = 0x04,
|
ADA_CCB_WAITING = 0x04,
|
||||||
ADA_CCB_DUMP = 0x05,
|
ADA_CCB_DUMP = 0x05,
|
||||||
@ -186,6 +188,10 @@ static void adashutdown(void *arg, int howto);
|
|||||||
#define ADA_DEFAULT_SPINDOWN_SHUTDOWN 1
|
#define ADA_DEFAULT_SPINDOWN_SHUTDOWN 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ADA_DEFAULT_WRITE_CACHE
|
||||||
|
#define ADA_DEFAULT_WRITE_CACHE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Most platforms map firmware geometry to actual, but some don't. If
|
* Most platforms map firmware geometry to actual, but some don't. If
|
||||||
* not overridden, default to nothing.
|
* not overridden, default to nothing.
|
||||||
@ -198,6 +204,7 @@ static int ada_retry_count = ADA_DEFAULT_RETRY;
|
|||||||
static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
|
static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
|
||||||
static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
|
static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
|
||||||
static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
|
static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
|
||||||
|
static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
|
||||||
|
|
||||||
SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
|
SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
|
||||||
"CAM Direct Access Disk driver");
|
"CAM Direct Access Disk driver");
|
||||||
@ -213,6 +220,9 @@ TUNABLE_INT("kern.cam.ada.ada_send_ordered", &ada_send_ordered);
|
|||||||
SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
|
SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
|
||||||
&ada_spindown_shutdown, 0, "Spin down upon shutdown");
|
&ada_spindown_shutdown, 0, "Spin down upon shutdown");
|
||||||
TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
|
TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
|
||||||
|
SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RW,
|
||||||
|
&ada_write_cache, 0, "Enable disk write cache");
|
||||||
|
TUNABLE_INT("kern.cam.ada.write_cache", &ada_write_cache);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ADA_ORDEREDTAG_INTERVAL determines how often, relative
|
* ADA_ORDEREDTAG_INTERVAL determines how often, relative
|
||||||
@ -568,6 +578,7 @@ adaasync(void *callback_arg, u_int32_t code,
|
|||||||
struct cam_path *path, void *arg)
|
struct cam_path *path, void *arg)
|
||||||
{
|
{
|
||||||
struct cam_periph *periph;
|
struct cam_periph *periph;
|
||||||
|
struct ada_softc *softc;
|
||||||
|
|
||||||
periph = (struct cam_periph *)callback_arg;
|
periph = (struct cam_periph *)callback_arg;
|
||||||
switch (code) {
|
switch (code) {
|
||||||
@ -600,6 +611,28 @@ adaasync(void *callback_arg, u_int32_t code,
|
|||||||
"due to status 0x%x\n", status);
|
"due to status 0x%x\n", status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AC_SENT_BDR:
|
||||||
|
case AC_BUS_RESET:
|
||||||
|
{
|
||||||
|
struct ccb_getdev cgd;
|
||||||
|
|
||||||
|
softc = (struct ada_softc *)periph->softc;
|
||||||
|
cam_periph_async(periph, code, path, arg);
|
||||||
|
if (ada_write_cache < 0)
|
||||||
|
break;
|
||||||
|
if (softc->state != ADA_STATE_NORMAL)
|
||||||
|
break;
|
||||||
|
xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL);
|
||||||
|
cgd.ccb_h.func_code = XPT_GDEV_TYPE;
|
||||||
|
xpt_action((union ccb *)&cgd);
|
||||||
|
if ((cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) == 0)
|
||||||
|
break;
|
||||||
|
softc->state = ADA_STATE_WCACHE;
|
||||||
|
cam_periph_acquire(periph);
|
||||||
|
cam_freeze_devq_arg(periph->path,
|
||||||
|
RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_DEV);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
cam_periph_async(periph, code, path, arg);
|
cam_periph_async(periph, code, path, arg);
|
||||||
break;
|
break;
|
||||||
@ -691,7 +724,6 @@ adaregister(struct cam_periph *periph, void *arg)
|
|||||||
}
|
}
|
||||||
if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
|
if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
|
||||||
softc->flags |= ADA_FLAG_CAN_CFA;
|
softc->flags |= ADA_FLAG_CAN_CFA;
|
||||||
softc->state = ADA_STATE_NORMAL;
|
|
||||||
|
|
||||||
periph->softc = softc;
|
periph->softc = softc;
|
||||||
|
|
||||||
@ -788,7 +820,7 @@ adaregister(struct cam_periph *periph, void *arg)
|
|||||||
* them and the only alternative would be to
|
* them and the only alternative would be to
|
||||||
* not attach the device on failure.
|
* not attach the device on failure.
|
||||||
*/
|
*/
|
||||||
xpt_register_async(AC_LOST_DEVICE,
|
xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
|
||||||
adaasync, periph, periph->path);
|
adaasync, periph, periph->path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -800,6 +832,16 @@ adaregister(struct cam_periph *periph, void *arg)
|
|||||||
(ADA_DEFAULT_TIMEOUT * hz) / ADA_ORDEREDTAG_INTERVAL,
|
(ADA_DEFAULT_TIMEOUT * hz) / ADA_ORDEREDTAG_INTERVAL,
|
||||||
adasendorderedtag, softc);
|
adasendorderedtag, softc);
|
||||||
|
|
||||||
|
if (ada_write_cache >= 0 &&
|
||||||
|
cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
|
||||||
|
softc->state = ADA_STATE_WCACHE;
|
||||||
|
cam_periph_acquire(periph);
|
||||||
|
cam_freeze_devq_arg(periph->path,
|
||||||
|
RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_DEV);
|
||||||
|
} else
|
||||||
|
softc->state = ADA_STATE_NORMAL;
|
||||||
|
|
||||||
return(CAM_REQ_CMP);
|
return(CAM_REQ_CMP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1009,6 +1051,23 @@ adastart(struct cam_periph *periph, union ccb *start_ccb)
|
|||||||
adaschedule(periph);
|
adaschedule(periph);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ADA_STATE_WCACHE:
|
||||||
|
{
|
||||||
|
cam_fill_ataio(ataio,
|
||||||
|
1,
|
||||||
|
adadone,
|
||||||
|
CAM_DIR_NONE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
ada_default_timeout*1000);
|
||||||
|
|
||||||
|
ata_28bit_cmd(ataio, ATA_SETFEATURES, ada_write_cache ?
|
||||||
|
ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0);
|
||||||
|
start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE;
|
||||||
|
xpt_action(start_ccb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1097,6 +1156,36 @@ adadone(struct cam_periph *periph, union ccb *done_ccb)
|
|||||||
biodone(bp);
|
biodone(bp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ADA_CCB_WCACHE:
|
||||||
|
{
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
if (adaerror(done_ccb, 0, 0) == ERESTART) {
|
||||||
|
return;
|
||||||
|
} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
|
||||||
|
cam_release_devq(done_ccb->ccb_h.path,
|
||||||
|
/*relsim_flags*/0,
|
||||||
|
/*reduction*/0,
|
||||||
|
/*timeout*/0,
|
||||||
|
/*getcount_only*/0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
softc->state = ADA_STATE_NORMAL;
|
||||||
|
/*
|
||||||
|
* Since our peripheral may be invalidated by an error
|
||||||
|
* above or an external event, we must release our CCB
|
||||||
|
* before releasing the reference on the peripheral.
|
||||||
|
* The peripheral will only go away once the last reference
|
||||||
|
* is removed, and we need it around for the CCB release
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
cam_release_devq(periph->path,
|
||||||
|
RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE);
|
||||||
|
adaschedule(periph);
|
||||||
|
cam_periph_release_locked(periph);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case ADA_CCB_WAITING:
|
case ADA_CCB_WAITING:
|
||||||
{
|
{
|
||||||
/* Caller will release the CCB */
|
/* Caller will release the CCB */
|
||||||
|
Loading…
Reference in New Issue
Block a user