Changeset View
Changeset View
Standalone View
Standalone View
head/sys/cam/scsi/scsi_da.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <string.h> | #include <string.h> | ||||
#endif /* _KERNEL */ | #endif /* _KERNEL */ | ||||
#include <cam/cam.h> | #include <cam/cam.h> | ||||
#include <cam/cam_ccb.h> | #include <cam/cam_ccb.h> | ||||
#include <cam/cam_periph.h> | #include <cam/cam_periph.h> | ||||
#include <cam/cam_xpt_periph.h> | #include <cam/cam_xpt_periph.h> | ||||
#include <cam/cam_sim.h> | #include <cam/cam_sim.h> | ||||
#include <cam/cam_iosched.h> | |||||
#include <cam/scsi/scsi_message.h> | #include <cam/scsi/scsi_message.h> | ||||
#ifndef _KERNEL | #ifndef _KERNEL | ||||
#include <cam/scsi/scsi_da.h> | #include <cam/scsi/scsi_da.h> | ||||
#endif /* !_KERNEL */ | #endif /* !_KERNEL */ | ||||
#ifdef _KERNEL | #ifdef _KERNEL | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | |||||
#define UNMAP_BUF_SIZE ((UNMAP_MAX_RANGES * UNMAP_RANGE_SIZE) + \ | #define UNMAP_BUF_SIZE ((UNMAP_MAX_RANGES * UNMAP_RANGE_SIZE) + \ | ||||
UNMAP_HEAD_SIZE) | UNMAP_HEAD_SIZE) | ||||
#define WS10_MAX_BLKS 0xffff | #define WS10_MAX_BLKS 0xffff | ||||
#define WS16_MAX_BLKS 0xffffffff | #define WS16_MAX_BLKS 0xffffffff | ||||
#define ATA_TRIM_MAX_RANGES ((UNMAP_BUF_SIZE / \ | #define ATA_TRIM_MAX_RANGES ((UNMAP_BUF_SIZE / \ | ||||
(ATA_DSM_RANGE_SIZE * ATA_DSM_BLK_SIZE)) * ATA_DSM_BLK_SIZE) | (ATA_DSM_RANGE_SIZE * ATA_DSM_BLK_SIZE)) * ATA_DSM_BLK_SIZE) | ||||
#define DA_WORK_TUR (1 << 16) | |||||
struct da_softc { | struct da_softc { | ||||
struct bio_queue_head bio_queue; | struct cam_iosched_softc *cam_iosched; | ||||
struct bio_queue_head delete_queue; | |||||
struct bio_queue_head delete_run_queue; | struct bio_queue_head delete_run_queue; | ||||
LIST_HEAD(, ccb_hdr) pending_ccbs; | LIST_HEAD(, ccb_hdr) pending_ccbs; | ||||
int tur; /* TEST UNIT READY should be sent */ | |||||
int refcount; /* Active xpt_action() calls */ | int refcount; /* Active xpt_action() calls */ | ||||
da_state state; | da_state state; | ||||
da_flags flags; | da_flags flags; | ||||
da_quirks quirks; | da_quirks quirks; | ||||
int sort_io_queue; | |||||
int minimum_cmd_size; | int minimum_cmd_size; | ||||
int error_inject; | int error_inject; | ||||
int trim_max_ranges; | int trim_max_ranges; | ||||
int delete_running; | |||||
int delete_available; /* Delete methods possibly available */ | int delete_available; /* Delete methods possibly available */ | ||||
u_int maxio; | u_int maxio; | ||||
uint32_t unmap_max_ranges; | uint32_t unmap_max_ranges; | ||||
uint32_t unmap_max_lba; /* Max LBAs in UNMAP req */ | uint32_t unmap_max_lba; /* Max LBAs in UNMAP req */ | ||||
uint64_t ws_max_blks; | uint64_t ws_max_blks; | ||||
da_delete_methods delete_method_pref; | da_delete_methods delete_method_pref; | ||||
da_delete_methods delete_method; | da_delete_methods delete_method; | ||||
da_delete_func_t *delete_func; | da_delete_func_t *delete_func; | ||||
int unmappedio; | |||||
int rotating; | |||||
struct disk_params params; | struct disk_params params; | ||||
struct disk *disk; | struct disk *disk; | ||||
union ccb saved_ccb; | union ccb saved_ccb; | ||||
struct task sysctl_task; | struct task sysctl_task; | ||||
struct sysctl_ctx_list sysctl_ctx; | struct sysctl_ctx_list sysctl_ctx; | ||||
struct sysctl_oid *sysctl_tree; | struct sysctl_oid *sysctl_tree; | ||||
struct callout sendordered_c; | struct callout sendordered_c; | ||||
uint64_t wwpn; | uint64_t wwpn; | ||||
uint8_t unmap_buf[UNMAP_BUF_SIZE]; | uint8_t unmap_buf[UNMAP_BUF_SIZE]; | ||||
struct scsi_read_capacity_data_long rcaplong; | struct scsi_read_capacity_data_long rcaplong; | ||||
struct callout mediapoll_c; | struct callout mediapoll_c; | ||||
#ifdef CAM_IO_STATS | |||||
struct sysctl_ctx_list sysctl_stats_ctx; | |||||
struct sysctl_oid *sysctl_stats_tree; | |||||
u_int errors; | |||||
u_int timeouts; | |||||
u_int invalidations; | |||||
#endif | |||||
}; | }; | ||||
#define dadeleteflag(softc, delete_method, enable) \ | #define dadeleteflag(softc, delete_method, enable) \ | ||||
if (enable) { \ | if (enable) { \ | ||||
softc->delete_available |= (1 << delete_method); \ | softc->delete_available |= (1 << delete_method); \ | ||||
} else { \ | } else { \ | ||||
softc->delete_available &= ~(1 << delete_method); \ | softc->delete_available &= ~(1 << delete_method); \ | ||||
} | } | ||||
▲ Show 20 Lines • Show All 944 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
static disk_strategy_t dastrategy; | static disk_strategy_t dastrategy; | ||||
static dumper_t dadump; | static dumper_t dadump; | ||||
static periph_init_t dainit; | static periph_init_t dainit; | ||||
static void daasync(void *callback_arg, u_int32_t code, | static void daasync(void *callback_arg, u_int32_t code, | ||||
struct cam_path *path, void *arg); | struct cam_path *path, void *arg); | ||||
static void dasysctlinit(void *context, int pending); | static void dasysctlinit(void *context, int pending); | ||||
static int dasysctlsofttimeout(SYSCTL_HANDLER_ARGS); | |||||
static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); | static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); | ||||
static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS); | static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS); | ||||
static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS); | static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS); | ||||
static void dadeletemethodset(struct da_softc *softc, | static void dadeletemethodset(struct da_softc *softc, | ||||
da_delete_methods delete_method); | da_delete_methods delete_method); | ||||
static off_t dadeletemaxsize(struct da_softc *softc, | static off_t dadeletemaxsize(struct da_softc *softc, | ||||
da_delete_methods delete_method); | da_delete_methods delete_method); | ||||
static void dadeletemethodchoose(struct da_softc *softc, | static void dadeletemethodchoose(struct da_softc *softc, | ||||
Show All 21 Lines | |||||
#ifndef DA_DEFAULT_POLL_PERIOD | #ifndef DA_DEFAULT_POLL_PERIOD | ||||
#define DA_DEFAULT_POLL_PERIOD 3 | #define DA_DEFAULT_POLL_PERIOD 3 | ||||
#endif | #endif | ||||
#ifndef DA_DEFAULT_TIMEOUT | #ifndef DA_DEFAULT_TIMEOUT | ||||
#define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ | #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ | ||||
#endif | #endif | ||||
#ifndef DA_DEFAULT_SOFTTIMEOUT | |||||
#define DA_DEFAULT_SOFTTIMEOUT 0 | |||||
#endif | |||||
#ifndef DA_DEFAULT_RETRY | #ifndef DA_DEFAULT_RETRY | ||||
#define DA_DEFAULT_RETRY 4 | #define DA_DEFAULT_RETRY 4 | ||||
#endif | #endif | ||||
#ifndef DA_DEFAULT_SEND_ORDERED | #ifndef DA_DEFAULT_SEND_ORDERED | ||||
#define DA_DEFAULT_SEND_ORDERED 1 | #define DA_DEFAULT_SEND_ORDERED 1 | ||||
#endif | #endif | ||||
#define DA_SIO (softc->sort_io_queue >= 0 ? \ | |||||
softc->sort_io_queue : cam_sort_io_queues) | |||||
static int da_poll_period = DA_DEFAULT_POLL_PERIOD; | static int da_poll_period = DA_DEFAULT_POLL_PERIOD; | ||||
static int da_retry_count = DA_DEFAULT_RETRY; | static int da_retry_count = DA_DEFAULT_RETRY; | ||||
static int da_default_timeout = DA_DEFAULT_TIMEOUT; | static int da_default_timeout = DA_DEFAULT_TIMEOUT; | ||||
static sbintime_t da_default_softtimeout = DA_DEFAULT_SOFTTIMEOUT; | |||||
static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; | static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; | ||||
static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, | static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, | ||||
"CAM Direct Access Disk driver"); | "CAM Direct Access Disk driver"); | ||||
SYSCTL_INT(_kern_cam_da, OID_AUTO, poll_period, CTLFLAG_RWTUN, | SYSCTL_INT(_kern_cam_da, OID_AUTO, poll_period, CTLFLAG_RWTUN, | ||||
&da_poll_period, 0, "Media polling period in seconds"); | &da_poll_period, 0, "Media polling period in seconds"); | ||||
SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RWTUN, | SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RWTUN, | ||||
&da_retry_count, 0, "Normal I/O retry count"); | &da_retry_count, 0, "Normal I/O retry count"); | ||||
SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RWTUN, | SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RWTUN, | ||||
&da_default_timeout, 0, "Normal I/O timeout (in seconds)"); | &da_default_timeout, 0, "Normal I/O timeout (in seconds)"); | ||||
SYSCTL_INT(_kern_cam_da, OID_AUTO, send_ordered, CTLFLAG_RWTUN, | SYSCTL_INT(_kern_cam_da, OID_AUTO, send_ordered, CTLFLAG_RWTUN, | ||||
&da_send_ordered, 0, "Send Ordered Tags"); | &da_send_ordered, 0, "Send Ordered Tags"); | ||||
SYSCTL_PROC(_kern_cam_da, OID_AUTO, default_softtimeout, | |||||
CTLTYPE_UINT | CTLFLAG_RW, NULL, 0, dasysctlsofttimeout, "I", | |||||
"Soft I/O timeout (ms)"); | |||||
TUNABLE_LONG("kern.cam.da.default_softtimeout", &da_default_softtimeout); | |||||
/* | /* | ||||
* DA_ORDEREDTAG_INTERVAL determines how often, relative | * DA_ORDEREDTAG_INTERVAL determines how often, relative | ||||
* to the default timeout, we check to see whether an ordered | * to the default timeout, we check to see whether an ordered | ||||
* tagged transaction is appropriate to prevent simple tag | * tagged transaction is appropriate to prevent simple tag | ||||
* starvation. Since we'd like to ensure that there is at least | * starvation. Since we'd like to ensure that there is at least | ||||
* 1/2 of the timeout length left for a starved transaction to | * 1/2 of the timeout length left for a starved transaction to | ||||
* complete after we've sent an ordered tag, we must poll at least | * complete after we've sent an ordered tag, we must poll at least | ||||
* four times in every timeout period. This takes care of the worst | * four times in every timeout period. This takes care of the worst | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
daschedule(struct cam_periph *periph) | daschedule(struct cam_periph *periph) | ||||
{ | { | ||||
struct da_softc *softc = (struct da_softc *)periph->softc; | struct da_softc *softc = (struct da_softc *)periph->softc; | ||||
if (softc->state != DA_STATE_NORMAL) | if (softc->state != DA_STATE_NORMAL) | ||||
return; | return; | ||||
/* Check if we have more work to do. */ | cam_iosched_schedule(softc->cam_iosched, periph); | ||||
if (bioq_first(&softc->bio_queue) || | |||||
(!softc->delete_running && bioq_first(&softc->delete_queue)) || | |||||
softc->tur) { | |||||
xpt_schedule(periph, CAM_PRIORITY_NORMAL); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Actually translate the requested transfer into one the physical driver | * Actually translate the requested transfer into one the physical driver | ||||
* can understand. The transfer is described by a buf and will include | * can understand. The transfer is described by a buf and will include | ||||
* only one physical transfer. | * only one physical transfer. | ||||
*/ | */ | ||||
static void | static void | ||||
dastrategy(struct bio *bp) | dastrategy(struct bio *bp) | ||||
Show All 15 Lines | if ((softc->flags & DA_FLAG_PACK_INVALID)) { | ||||
return; | return; | ||||
} | } | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dastrategy(%p)\n", bp)); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dastrategy(%p)\n", bp)); | ||||
/* | /* | ||||
* Place it in the queue of disk activities for this disk | * Place it in the queue of disk activities for this disk | ||||
*/ | */ | ||||
if (bp->bio_cmd == BIO_DELETE) { | cam_iosched_queue_work(softc->cam_iosched, bp); | ||||
bioq_disksort(&softc->delete_queue, bp); | |||||
} else if (DA_SIO) { | |||||
bioq_disksort(&softc->bio_queue, bp); | |||||
} else { | |||||
bioq_insert_tail(&softc->bio_queue, bp); | |||||
} | |||||
/* | /* | ||||
* Schedule ourselves for performing the work. | * Schedule ourselves for performing the work. | ||||
*/ | */ | ||||
daschedule(periph); | daschedule(periph); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) { | ||||
csio.ccb_h.ccb_state = DA_CCB_DUMP; | csio.ccb_h.ccb_state = DA_CCB_DUMP; | ||||
scsi_synchronize_cache(&csio, | scsi_synchronize_cache(&csio, | ||||
/*retries*/0, | /*retries*/0, | ||||
/*cbfcnp*/dadone, | /*cbfcnp*/dadone, | ||||
MSG_SIMPLE_Q_TAG, | MSG_SIMPLE_Q_TAG, | ||||
/*begin_lba*/0,/* Cover the whole disk */ | /*begin_lba*/0,/* Cover the whole disk */ | ||||
/*lb_count*/0, | /*lb_count*/0, | ||||
SSD_FULL_SIZE, | SSD_FULL_SIZE, | ||||
5 * 60 * 1000); | 5 * 1000); | ||||
xpt_polled_action((union ccb *)&csio); | xpt_polled_action((union ccb *)&csio); | ||||
error = cam_periph_error((union ccb *)&csio, | error = cam_periph_error((union ccb *)&csio, | ||||
0, SF_NO_RECOVERY | SF_NO_RETRY | SF_QUIET_IR, NULL); | 0, SF_NO_RECOVERY | SF_NO_RETRY | SF_QUIET_IR, NULL); | ||||
if ((csio.ccb_h.status & CAM_DEV_QFRZN) != 0) | if ((csio.ccb_h.status & CAM_DEV_QFRZN) != 0) | ||||
cam_release_devq(csio.ccb_h.path, /*relsim_flags*/0, | cam_release_devq(csio.ccb_h.path, /*relsim_flags*/0, | ||||
/*reduction*/0, /*timeout*/0, /*getcount_only*/0); | /*reduction*/0, /*timeout*/0, /*getcount_only*/0); | ||||
if (error != 0) | if (error != 0) | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | daoninvalidate(struct cam_periph *periph) | ||||
softc = (struct da_softc *)periph->softc; | softc = (struct da_softc *)periph->softc; | ||||
/* | /* | ||||
* De-register any async callbacks. | * De-register any async callbacks. | ||||
*/ | */ | ||||
xpt_register_async(0, daasync, periph, periph->path); | xpt_register_async(0, daasync, periph, periph->path); | ||||
softc->flags |= DA_FLAG_PACK_INVALID; | softc->flags |= DA_FLAG_PACK_INVALID; | ||||
#ifdef CAM_IO_STATS | |||||
softc->invalidations++; | |||||
#endif | |||||
/* | /* | ||||
* Return all queued I/O with ENXIO. | * Return all queued I/O with ENXIO. | ||||
* XXX Handle any transactions queued to the card | * XXX Handle any transactions queued to the card | ||||
* with XPT_ABORT_CCB. | * with XPT_ABORT_CCB. | ||||
*/ | */ | ||||
bioq_flush(&softc->bio_queue, NULL, ENXIO); | cam_iosched_flush(softc->cam_iosched, NULL, ENXIO); | ||||
bioq_flush(&softc->delete_queue, NULL, ENXIO); | |||||
/* | /* | ||||
* Tell GEOM that we've gone away, we'll get a callback when it is | * Tell GEOM that we've gone away, we'll get a callback when it is | ||||
* done cleaning up its resources. | * done cleaning up its resources. | ||||
*/ | */ | ||||
disk_gone(softc->disk); | disk_gone(softc->disk); | ||||
} | } | ||||
static void | static void | ||||
dacleanup(struct cam_periph *periph) | dacleanup(struct cam_periph *periph) | ||||
{ | { | ||||
struct da_softc *softc; | struct da_softc *softc; | ||||
softc = (struct da_softc *)periph->softc; | softc = (struct da_softc *)periph->softc; | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
cam_iosched_fini(softc->cam_iosched); | |||||
/* | /* | ||||
* If we can't free the sysctl tree, oh well... | * If we can't free the sysctl tree, oh well... | ||||
*/ | */ | ||||
if ((softc->flags & DA_FLAG_SCTX_INIT) != 0 | if ((softc->flags & DA_FLAG_SCTX_INIT) != 0) { | ||||
&& sysctl_ctx_free(&softc->sysctl_ctx) != 0) { | #ifdef CAM_IO_STATS | ||||
xpt_print(periph->path, "can't remove sysctl context\n"); | if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0) | ||||
xpt_print(periph->path, | |||||
"can't remove sysctl stats context\n"); | |||||
#endif | |||||
if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) | |||||
xpt_print(periph->path, | |||||
"can't remove sysctl context\n"); | |||||
} | } | ||||
callout_drain(&softc->mediapoll_c); | callout_drain(&softc->mediapoll_c); | ||||
disk_destroy(softc->disk); | disk_destroy(softc->disk); | ||||
callout_drain(&softc->sendordered_c); | callout_drain(&softc->sendordered_c); | ||||
free(softc, M_DEVBUF); | free(softc, M_DEVBUF); | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | if (xpt_path_periph(ccb->ccb_h.path) != periph && | ||||
dareprobe(periph); | dareprobe(periph); | ||||
} | } | ||||
} | } | ||||
cam_periph_async(periph, code, path, arg); | cam_periph_async(periph, code, path, arg); | ||||
break; | break; | ||||
} | } | ||||
case AC_SCSI_AEN: | case AC_SCSI_AEN: | ||||
softc = (struct da_softc *)periph->softc; | softc = (struct da_softc *)periph->softc; | ||||
if (!softc->tur) { | if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { | ||||
if (cam_periph_acquire(periph) == CAM_REQ_CMP) { | if (cam_periph_acquire(periph) == CAM_REQ_CMP) { | ||||
softc->tur = 1; | cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); | ||||
daschedule(periph); | daschedule(periph); | ||||
} | } | ||||
} | } | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case AC_SENT_BDR: | case AC_SENT_BDR: | ||||
case AC_BUS_RESET: | case AC_BUS_RESET: | ||||
{ | { | ||||
struct ccb_hdr *ccbh; | struct ccb_hdr *ccbh; | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | dasysctlinit(void *context, int pending) | ||||
SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW, | OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW, | ||||
softc, 0, dadeletemaxsysctl, "Q", | softc, 0, dadeletemaxsysctl, "Q", | ||||
"Maximum BIO_DELETE size"); | "Maximum BIO_DELETE size"); | ||||
SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, | OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, | ||||
&softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", | &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", | ||||
"Minimum CDB size"); | "Minimum CDB size"); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | |||||
OID_AUTO, "sort_io_queue", CTLFLAG_RW, &softc->sort_io_queue, 0, | |||||
"Sort IO queue to try and optimise disk access patterns"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, | SYSCTL_ADD_INT(&softc->sysctl_ctx, | ||||
SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, | OID_AUTO, | ||||
"error_inject", | "error_inject", | ||||
CTLFLAG_RW, | CTLFLAG_RW, | ||||
&softc->error_inject, | &softc->error_inject, | ||||
0, | 0, | ||||
"error_inject leaf"); | "error_inject leaf"); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_tree), | |||||
OID_AUTO, | |||||
"unmapped_io", | |||||
CTLFLAG_RD, | |||||
&softc->unmappedio, | |||||
0, | |||||
"Unmapped I/O leaf"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_tree), | |||||
OID_AUTO, | |||||
"rotating", | |||||
CTLFLAG_RD, | |||||
&softc->rotating, | |||||
0, | |||||
"Rotating media"); | |||||
/* | /* | ||||
* Add some addressing info. | * Add some addressing info. | ||||
*/ | */ | ||||
memset(&cts, 0, sizeof (cts)); | memset(&cts, 0, sizeof (cts)); | ||||
xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); | xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); | ||||
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; | cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; | ||||
cts.type = CTS_TYPE_CURRENT_SETTINGS; | cts.type = CTS_TYPE_CURRENT_SETTINGS; | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
xpt_action((union ccb *)&cts); | xpt_action((union ccb *)&cts); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
if (cts.ccb_h.status != CAM_REQ_CMP) { | if (cts.ccb_h.status != CAM_REQ_CMP) { | ||||
cam_periph_release(periph); | cam_periph_release(periph); | ||||
return; | return; | ||||
} | } | ||||
if (cts.protocol == PROTO_SCSI && cts.transport == XPORT_FC) { | if (cts.protocol == PROTO_SCSI && cts.transport == XPORT_FC) { | ||||
struct ccb_trans_settings_fc *fc = &cts.xport_specific.fc; | struct ccb_trans_settings_fc *fc = &cts.xport_specific.fc; | ||||
if (fc->valid & CTS_FC_VALID_WWPN) { | if (fc->valid & CTS_FC_VALID_WWPN) { | ||||
softc->wwpn = fc->wwpn; | softc->wwpn = fc->wwpn; | ||||
SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, | SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, | ||||
SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "wwpn", CTLFLAG_RD, | OID_AUTO, "wwpn", CTLFLAG_RD, | ||||
&softc->wwpn, "World Wide Port Name"); | &softc->wwpn, "World Wide Port Name"); | ||||
} | } | ||||
} | } | ||||
#ifdef CAM_IO_STATS | |||||
/* | |||||
* Now add some useful stats. | |||||
* XXX These should live in cam_periph and be common to all periphs | |||||
*/ | |||||
softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats", | |||||
CTLFLAG_RD, 0, "Statistics"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_stats_tree), | |||||
OID_AUTO, | |||||
"errors", | |||||
CTLFLAG_RD, | |||||
&softc->errors, | |||||
0, | |||||
"Transport errors reported by the SIM"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_stats_tree), | |||||
OID_AUTO, | |||||
"timeouts", | |||||
CTLFLAG_RD, | |||||
&softc->timeouts, | |||||
0, | |||||
"Device timeouts reported by the SIM"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_stats_tree), | |||||
OID_AUTO, | |||||
"pack_invalidations", | |||||
CTLFLAG_RD, | |||||
&softc->invalidations, | |||||
0, | |||||
"Device pack invalidations"); | |||||
#endif | |||||
cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, | |||||
softc->sysctl_tree); | |||||
cam_periph_release(periph); | cam_periph_release(periph); | ||||
} | } | ||||
static int | static int | ||||
dadeletemaxsysctl(SYSCTL_HANDLER_ARGS) | dadeletemaxsysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | int error; | ||||
uint64_t value; | uint64_t value; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | dacmdsizesysctl(SYSCTL_HANDLER_ARGS) | ||||
else if (value > 12) | else if (value > 12) | ||||
value = 16; | value = 16; | ||||
*(int *)arg1 = value; | *(int *)arg1 = value; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
dasysctlsofttimeout(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
sbintime_t value; | |||||
int error; | |||||
value = da_default_softtimeout / SBT_1MS; | |||||
error = sysctl_handle_int(oidp, (int *)&value, 0, req); | |||||
if ((error != 0) || (req->newptr == NULL)) | |||||
return (error); | |||||
/* XXX Should clip this to a reasonable level */ | |||||
if (value > da_default_timeout * 1000) | |||||
return (EINVAL); | |||||
da_default_softtimeout = value * SBT_1MS; | |||||
return (0); | |||||
} | |||||
static void | static void | ||||
dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) | dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) | ||||
{ | { | ||||
softc->delete_method = delete_method; | softc->delete_method = delete_method; | ||||
softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method); | softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method); | ||||
softc->delete_func = da_delete_functions[delete_method]; | softc->delete_func = da_delete_functions[delete_method]; | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | if (cgd == NULL) { | ||||
return(CAM_REQ_CMP_ERR); | return(CAM_REQ_CMP_ERR); | ||||
} | } | ||||
softc = (struct da_softc *)malloc(sizeof(*softc), M_DEVBUF, | softc = (struct da_softc *)malloc(sizeof(*softc), M_DEVBUF, | ||||
M_NOWAIT|M_ZERO); | M_NOWAIT|M_ZERO); | ||||
if (softc == NULL) { | if (softc == NULL) { | ||||
printf("daregister: Unable to probe new device. " | printf("daregister: Unable to probe new device. " | ||||
"Unable to allocate softc\n"); | "Unable to allocate softc\n"); | ||||
return(CAM_REQ_CMP_ERR); | return(CAM_REQ_CMP_ERR); | ||||
} | } | ||||
if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { | |||||
printf("daregister: Unable to probe new device. " | |||||
"Unable to allocate iosched memory\n"); | |||||
return(CAM_REQ_CMP_ERR); | |||||
} | |||||
LIST_INIT(&softc->pending_ccbs); | LIST_INIT(&softc->pending_ccbs); | ||||
softc->state = DA_STATE_PROBE_RC; | softc->state = DA_STATE_PROBE_RC; | ||||
bioq_init(&softc->bio_queue); | |||||
bioq_init(&softc->delete_queue); | |||||
bioq_init(&softc->delete_run_queue); | bioq_init(&softc->delete_run_queue); | ||||
if (SID_IS_REMOVABLE(&cgd->inq_data)) | if (SID_IS_REMOVABLE(&cgd->inq_data)) | ||||
softc->flags |= DA_FLAG_PACK_REMOVABLE; | softc->flags |= DA_FLAG_PACK_REMOVABLE; | ||||
softc->unmap_max_ranges = UNMAP_MAX_RANGES; | softc->unmap_max_ranges = UNMAP_MAX_RANGES; | ||||
softc->unmap_max_lba = UNMAP_RANGE_MAX; | softc->unmap_max_lba = UNMAP_RANGE_MAX; | ||||
softc->ws_max_blks = WS16_MAX_BLKS; | softc->ws_max_blks = WS16_MAX_BLKS; | ||||
softc->trim_max_ranges = ATA_TRIM_MAX_RANGES; | softc->trim_max_ranges = ATA_TRIM_MAX_RANGES; | ||||
softc->sort_io_queue = -1; | softc->rotating = 1; | ||||
periph->softc = softc; | periph->softc = softc; | ||||
/* | /* | ||||
* See if this device has any quirks. | * See if this device has any quirks. | ||||
*/ | */ | ||||
match = cam_quirkmatch((caddr_t)&cgd->inq_data, | match = cam_quirkmatch((caddr_t)&cgd->inq_data, | ||||
(caddr_t)da_quirk_table, | (caddr_t)da_quirk_table, | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | else if (cpi.maxio > MAXPHYS) | ||||
softc->maxio = MAXPHYS; /* for safety */ | softc->maxio = MAXPHYS; /* for safety */ | ||||
else | else | ||||
softc->maxio = cpi.maxio; | softc->maxio = cpi.maxio; | ||||
softc->disk->d_maxsize = softc->maxio; | softc->disk->d_maxsize = softc->maxio; | ||||
softc->disk->d_unit = periph->unit_number; | softc->disk->d_unit = periph->unit_number; | ||||
softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION; | softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION; | ||||
if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) | if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) | ||||
softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; | softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; | ||||
if ((cpi.hba_misc & PIM_UNMAPPED) != 0) | if ((cpi.hba_misc & PIM_UNMAPPED) != 0) { | ||||
softc->unmappedio = 1; | |||||
softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; | softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; | ||||
xpt_print(periph->path, "UNMAPPED\n"); | |||||
} | |||||
cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, | cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, | ||||
sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); | sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); | ||||
strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); | strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); | ||||
cam_strvis(&softc->disk->d_descr[strlen(softc->disk->d_descr)], | cam_strvis(&softc->disk->d_descr[strlen(softc->disk->d_descr)], | ||||
cgd->inq_data.product, sizeof(cgd->inq_data.product), | cgd->inq_data.product, sizeof(cgd->inq_data.product), | ||||
sizeof(softc->disk->d_descr) - strlen(softc->disk->d_descr)); | sizeof(softc->disk->d_descr) - strlen(softc->disk->d_descr)); | ||||
softc->disk->d_hba_vendor = cpi.hba_vendor; | softc->disk->d_hba_vendor = cpi.hba_vendor; | ||||
softc->disk->d_hba_device = cpi.hba_device; | softc->disk->d_hba_device = cpi.hba_device; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
skipstate: | skipstate: | ||||
switch (softc->state) { | switch (softc->state) { | ||||
case DA_STATE_NORMAL: | case DA_STATE_NORMAL: | ||||
{ | { | ||||
struct bio *bp; | struct bio *bp; | ||||
uint8_t tag_code; | uint8_t tag_code; | ||||
/* Run BIO_DELETE if not running yet. */ | more: | ||||
if (!softc->delete_running && | bp = cam_iosched_next_bio(softc->cam_iosched); | ||||
(bp = bioq_first(&softc->delete_queue)) != NULL) { | |||||
if (softc->delete_func != NULL) { | |||||
softc->delete_func(periph, start_ccb, bp); | |||||
goto out; | |||||
} else { | |||||
bioq_flush(&softc->delete_queue, NULL, 0); | |||||
/* FALLTHROUGH */ | |||||
} | |||||
} | |||||
/* Run regular command. */ | |||||
bp = bioq_takefirst(&softc->bio_queue); | |||||
if (bp == NULL) { | if (bp == NULL) { | ||||
if (softc->tur) { | if (cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { | ||||
softc->tur = 0; | cam_iosched_clr_work_flags(softc->cam_iosched, DA_WORK_TUR); | ||||
scsi_test_unit_ready(&start_ccb->csio, | scsi_test_unit_ready(&start_ccb->csio, | ||||
/*retries*/ da_retry_count, | /*retries*/ da_retry_count, | ||||
dadone, | dadone, | ||||
MSG_SIMPLE_Q_TAG, | MSG_SIMPLE_Q_TAG, | ||||
SSD_FULL_SIZE, | SSD_FULL_SIZE, | ||||
da_default_timeout * 1000); | da_default_timeout * 1000); | ||||
start_ccb->ccb_h.ccb_bp = NULL; | start_ccb->ccb_h.ccb_bp = NULL; | ||||
start_ccb->ccb_h.ccb_state = DA_CCB_TUR; | start_ccb->ccb_h.ccb_state = DA_CCB_TUR; | ||||
xpt_action(start_ccb); | xpt_action(start_ccb); | ||||
} else | } else | ||||
xpt_release_ccb(start_ccb); | xpt_release_ccb(start_ccb); | ||||
break; | break; | ||||
} | } | ||||
if (softc->tur) { | |||||
softc->tur = 0; | if (bp->bio_cmd == BIO_DELETE) { | ||||
cam_periph_release_locked(periph); | if (softc->delete_func != NULL) { | ||||
softc->delete_func(periph, start_ccb, bp); | |||||
goto out; | |||||
} else { | |||||
/* Not sure this is possible, but failsafe by lying and saying "sure, done." */ | |||||
biofinish(bp, NULL, 0); | |||||
goto more; | |||||
} | } | ||||
} | |||||
if (cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { | |||||
cam_iosched_clr_work_flags(softc->cam_iosched, DA_WORK_TUR); | |||||
cam_periph_release_locked(periph); /* XXX is this still valid? I think so but unverified */ | |||||
} | |||||
if ((bp->bio_flags & BIO_ORDERED) != 0 || | if ((bp->bio_flags & BIO_ORDERED) != 0 || | ||||
(softc->flags & DA_FLAG_NEED_OTAG) != 0) { | (softc->flags & DA_FLAG_NEED_OTAG) != 0) { | ||||
softc->flags &= ~DA_FLAG_NEED_OTAG; | softc->flags &= ~DA_FLAG_NEED_OTAG; | ||||
softc->flags |= DA_FLAG_WAS_OTAG; | softc->flags |= DA_FLAG_WAS_OTAG; | ||||
tag_code = MSG_ORDERED_Q_TAG; | tag_code = MSG_ORDERED_Q_TAG; | ||||
} else { | } else { | ||||
tag_code = MSG_SIMPLE_Q_TAG; | tag_code = MSG_SIMPLE_Q_TAG; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | case BIO_FLUSH: | ||||
/*begin_lba*/0, | /*begin_lba*/0, | ||||
/*lb_count*/0, | /*lb_count*/0, | ||||
SSD_FULL_SIZE, | SSD_FULL_SIZE, | ||||
da_default_timeout*1000); | da_default_timeout*1000); | ||||
break; | break; | ||||
} | } | ||||
start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO; | start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO; | ||||
start_ccb->ccb_h.flags |= CAM_UNLOCKED; | start_ccb->ccb_h.flags |= CAM_UNLOCKED; | ||||
start_ccb->ccb_h.softtimeout = sbttotv(da_default_softtimeout); | |||||
out: | out: | ||||
LIST_INSERT_HEAD(&softc->pending_ccbs, | LIST_INSERT_HEAD(&softc->pending_ccbs, | ||||
&start_ccb->ccb_h, periph_links.le); | &start_ccb->ccb_h, periph_links.le); | ||||
/* We expect a unit attention from this device */ | /* We expect a unit attention from this device */ | ||||
if ((softc->flags & DA_FLAG_RETRY_UA) != 0) { | if ((softc->flags & DA_FLAG_RETRY_UA) != 0) { | ||||
start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA; | start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA; | ||||
▲ Show 20 Lines • Show All 232 Lines • ▼ Show 20 Lines | da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp) | ||||
* Granularity and Granularity Alignment | * Granularity and Granularity Alignment | ||||
* fields into account. | * fields into account. | ||||
* | * | ||||
* This could result in both unoptimal unmap | * This could result in both unoptimal unmap | ||||
* requests as as well as UNMAP calls unmapping | * requests as as well as UNMAP calls unmapping | ||||
* fewer LBA's than requested. | * fewer LBA's than requested. | ||||
*/ | */ | ||||
softc->delete_running = 1; | |||||
bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); | bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); | ||||
bp1 = bp; | bp1 = bp; | ||||
do { | do { | ||||
bioq_remove(&softc->delete_queue, bp1); | /* | ||||
* Note: ada and da are different in how they store the | |||||
* pending bp's in a trim. ada stores all of them in the | |||||
* trim_req.bps. da stores all but the first one in the | |||||
* delete_run_queue. ada then completes all the bps in | |||||
* its adadone() loop. da completes all the bps in the | |||||
* delete_run_queue in dadone, and relies on the biodone | |||||
* after to complete. This should be reconciled since there's | |||||
* no real reason to do it differently. XXX | |||||
*/ | |||||
if (bp1 != bp) | if (bp1 != bp) | ||||
bioq_insert_tail(&softc->delete_run_queue, bp1); | bioq_insert_tail(&softc->delete_run_queue, bp1); | ||||
lba = bp1->bio_pblkno; | lba = bp1->bio_pblkno; | ||||
count = bp1->bio_bcount / softc->params.secsize; | count = bp1->bio_bcount / softc->params.secsize; | ||||
/* Try to extend the previous range. */ | /* Try to extend the previous range. */ | ||||
if (lba == lastlba) { | if (lba == lastlba) { | ||||
c = omin(count, UNMAP_RANGE_MAX - lastcount); | c = omin(count, UNMAP_RANGE_MAX - lastcount); | ||||
Show All 23 Lines | while (count > 0) { | ||||
scsi_ulto4b(c, &buf[off + 8]); | scsi_ulto4b(c, &buf[off + 8]); | ||||
lba += c; | lba += c; | ||||
totalcount += c; | totalcount += c; | ||||
ranges++; | ranges++; | ||||
count -= c; | count -= c; | ||||
lastcount = c; | lastcount = c; | ||||
} | } | ||||
lastlba = lba; | lastlba = lba; | ||||
bp1 = bioq_first(&softc->delete_queue); | bp1 = cam_iosched_next_trim(softc->cam_iosched); | ||||
if (bp1 == NULL || ranges >= softc->unmap_max_ranges || | if (bp1 == NULL) | ||||
break; | |||||
if (ranges >= softc->unmap_max_ranges || | |||||
totalcount + bp1->bio_bcount / | totalcount + bp1->bio_bcount / | ||||
softc->params.secsize > softc->unmap_max_lba) | softc->params.secsize > softc->unmap_max_lba) { | ||||
cam_iosched_put_back_trim(softc->cam_iosched, bp1); | |||||
break; | break; | ||||
} | |||||
} while (1); | } while (1); | ||||
scsi_ulto2b(ranges * 16 + 6, &buf[0]); | scsi_ulto2b(ranges * 16 + 6, &buf[0]); | ||||
scsi_ulto2b(ranges * 16, &buf[2]); | scsi_ulto2b(ranges * 16, &buf[2]); | ||||
scsi_unmap(&ccb->csio, | scsi_unmap(&ccb->csio, | ||||
/*retries*/da_retry_count, | /*retries*/da_retry_count, | ||||
/*cbfcnp*/dadone, | /*cbfcnp*/dadone, | ||||
/*tag_action*/MSG_SIMPLE_Q_TAG, | /*tag_action*/MSG_SIMPLE_Q_TAG, | ||||
/*byte2*/0, | /*byte2*/0, | ||||
/*data_ptr*/ buf, | /*data_ptr*/ buf, | ||||
/*dxfer_len*/ ranges * 16 + 8, | /*dxfer_len*/ ranges * 16 + 8, | ||||
/*sense_len*/SSD_FULL_SIZE, | /*sense_len*/SSD_FULL_SIZE, | ||||
da_default_timeout * 1000); | da_default_timeout * 1000); | ||||
ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ||||
ccb->ccb_h.flags |= CAM_UNLOCKED; | ccb->ccb_h.flags |= CAM_UNLOCKED; | ||||
cam_iosched_submit_trim(softc->cam_iosched); | |||||
} | } | ||||
static void | static void | ||||
da_delete_trim(struct cam_periph *periph, union ccb *ccb, struct bio *bp) | da_delete_trim(struct cam_periph *periph, union ccb *ccb, struct bio *bp) | ||||
{ | { | ||||
struct da_softc *softc = (struct da_softc *)periph->softc; | struct da_softc *softc = (struct da_softc *)periph->softc; | ||||
struct bio *bp1; | struct bio *bp1; | ||||
uint8_t *buf = softc->unmap_buf; | uint8_t *buf = softc->unmap_buf; | ||||
uint64_t lastlba = (uint64_t)-1; | uint64_t lastlba = (uint64_t)-1; | ||||
uint64_t count; | uint64_t count; | ||||
uint64_t lba; | uint64_t lba; | ||||
uint32_t lastcount = 0, c, requestcount; | uint32_t lastcount = 0, c, requestcount; | ||||
int ranges = 0, off, block_count; | int ranges = 0, off, block_count; | ||||
softc->delete_running = 1; | |||||
bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); | bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); | ||||
bp1 = bp; | bp1 = bp; | ||||
do { | do { | ||||
bioq_remove(&softc->delete_queue, bp1); | if (bp1 != bp)//XXX imp XXX | ||||
ngie: XXX comment? | |||||
if (bp1 != bp) | |||||
bioq_insert_tail(&softc->delete_run_queue, bp1); | bioq_insert_tail(&softc->delete_run_queue, bp1); | ||||
lba = bp1->bio_pblkno; | lba = bp1->bio_pblkno; | ||||
count = bp1->bio_bcount / softc->params.secsize; | count = bp1->bio_bcount / softc->params.secsize; | ||||
requestcount = count; | requestcount = count; | ||||
/* Try to extend the previous range. */ | /* Try to extend the previous range. */ | ||||
if (lba == lastlba) { | if (lba == lastlba) { | ||||
c = omin(count, ATA_DSM_RANGE_MAX - lastcount); | c = omin(count, ATA_DSM_RANGE_MAX - lastcount); | ||||
Show All 27 Lines | while (count > 0) { | ||||
da_delete_method_desc[softc->delete_method], | da_delete_method_desc[softc->delete_method], | ||||
requestcount, | requestcount, | ||||
(softc->trim_max_ranges - ranges) * | (softc->trim_max_ranges - ranges) * | ||||
ATA_DSM_RANGE_MAX); | ATA_DSM_RANGE_MAX); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
lastlba = lba; | lastlba = lba; | ||||
bp1 = bioq_first(&softc->delete_queue); | bp1 = cam_iosched_next_trim(softc->cam_iosched); | ||||
if (bp1 == NULL || bp1->bio_bcount / softc->params.secsize > | if (bp1 == NULL) | ||||
(softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) | |||||
break; | break; | ||||
if (bp1->bio_bcount / softc->params.secsize > | |||||
(softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { | |||||
cam_iosched_put_back_trim(softc->cam_iosched, bp1); | |||||
break; | |||||
} | |||||
} while (1); | } while (1); | ||||
block_count = (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES; | block_count = (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES; | ||||
scsi_ata_trim(&ccb->csio, | scsi_ata_trim(&ccb->csio, | ||||
/*retries*/da_retry_count, | /*retries*/da_retry_count, | ||||
/*cbfcnp*/dadone, | /*cbfcnp*/dadone, | ||||
/*tag_action*/MSG_SIMPLE_Q_TAG, | /*tag_action*/MSG_SIMPLE_Q_TAG, | ||||
block_count, | block_count, | ||||
/*data_ptr*/buf, | /*data_ptr*/buf, | ||||
/*dxfer_len*/block_count * ATA_DSM_BLK_SIZE, | /*dxfer_len*/block_count * ATA_DSM_BLK_SIZE, | ||||
/*sense_len*/SSD_FULL_SIZE, | /*sense_len*/SSD_FULL_SIZE, | ||||
da_default_timeout * 1000); | da_default_timeout * 1000); | ||||
ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ||||
ccb->ccb_h.flags |= CAM_UNLOCKED; | ccb->ccb_h.flags |= CAM_UNLOCKED; | ||||
cam_iosched_submit_trim(softc->cam_iosched); | |||||
} | } | ||||
/* | /* | ||||
* We calculate ws_max_blks here based off d_delmaxsize instead | * We calculate ws_max_blks here based off d_delmaxsize instead | ||||
* of using softc->ws_max_blks as it is absolute max for the | * of using softc->ws_max_blks as it is absolute max for the | ||||
* device not the protocol max which may well be lower. | * device not the protocol max which may well be lower. | ||||
*/ | */ | ||||
static void | static void | ||||
da_delete_ws(struct cam_periph *periph, union ccb *ccb, struct bio *bp) | da_delete_ws(struct cam_periph *periph, union ccb *ccb, struct bio *bp) | ||||
{ | { | ||||
struct da_softc *softc; | struct da_softc *softc; | ||||
struct bio *bp1; | struct bio *bp1; | ||||
uint64_t ws_max_blks; | uint64_t ws_max_blks; | ||||
uint64_t lba; | uint64_t lba; | ||||
uint64_t count; /* forward compat with WS32 */ | uint64_t count; /* forward compat with WS32 */ | ||||
softc = (struct da_softc *)periph->softc; | softc = (struct da_softc *)periph->softc; | ||||
ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize; | ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize; | ||||
softc->delete_running = 1; | |||||
lba = bp->bio_pblkno; | lba = bp->bio_pblkno; | ||||
count = 0; | count = 0; | ||||
bp1 = bp; | bp1 = bp; | ||||
do { | do { | ||||
bioq_remove(&softc->delete_queue, bp1); | if (bp1 != bp)//XXX imp XXX | ||||
ngieUnsubmitted Not Done Inline ActionsXXX comment in C++? ngie: XXX comment in C++? | |||||
if (bp1 != bp) | |||||
bioq_insert_tail(&softc->delete_run_queue, bp1); | bioq_insert_tail(&softc->delete_run_queue, bp1); | ||||
count += bp1->bio_bcount / softc->params.secsize; | count += bp1->bio_bcount / softc->params.secsize; | ||||
if (count > ws_max_blks) { | if (count > ws_max_blks) { | ||||
xpt_print(periph->path, | xpt_print(periph->path, | ||||
"%s issuing short delete %ld > %ld\n", | "%s issuing short delete %ld > %ld\n", | ||||
da_delete_method_desc[softc->delete_method], | da_delete_method_desc[softc->delete_method], | ||||
count, ws_max_blks); | count, ws_max_blks); | ||||
count = omin(count, ws_max_blks); | count = omin(count, ws_max_blks); | ||||
break; | break; | ||||
} | } | ||||
bp1 = bioq_first(&softc->delete_queue); | bp1 = cam_iosched_next_trim(softc->cam_iosched); | ||||
if (bp1 == NULL || lba + count != bp1->bio_pblkno || | if (bp1 == NULL) | ||||
break; | |||||
if (lba + count != bp1->bio_pblkno || | |||||
count + bp1->bio_bcount / | count + bp1->bio_bcount / | ||||
softc->params.secsize > ws_max_blks) | softc->params.secsize > ws_max_blks) { | ||||
cam_iosched_put_back_trim(softc->cam_iosched, bp1); | |||||
break; | break; | ||||
} | |||||
} while (1); | } while (1); | ||||
scsi_write_same(&ccb->csio, | scsi_write_same(&ccb->csio, | ||||
/*retries*/da_retry_count, | /*retries*/da_retry_count, | ||||
/*cbfcnp*/dadone, | /*cbfcnp*/dadone, | ||||
/*tag_action*/MSG_SIMPLE_Q_TAG, | /*tag_action*/MSG_SIMPLE_Q_TAG, | ||||
/*byte2*/softc->delete_method == | /*byte2*/softc->delete_method == | ||||
DA_DELETE_ZERO ? 0 : SWS_UNMAP, | DA_DELETE_ZERO ? 0 : SWS_UNMAP, | ||||
softc->delete_method == DA_DELETE_WS16 ? 16 : 10, | softc->delete_method == DA_DELETE_WS16 ? 16 : 10, | ||||
/*lba*/lba, | /*lba*/lba, | ||||
/*block_count*/count, | /*block_count*/count, | ||||
/*data_ptr*/ __DECONST(void *, zero_region), | /*data_ptr*/ __DECONST(void *, zero_region), | ||||
/*dxfer_len*/ softc->params.secsize, | /*dxfer_len*/ softc->params.secsize, | ||||
/*sense_len*/SSD_FULL_SIZE, | /*sense_len*/SSD_FULL_SIZE, | ||||
da_default_timeout * 1000); | da_default_timeout * 1000); | ||||
ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ccb->ccb_h.ccb_state = DA_CCB_DELETE; | ||||
ccb->ccb_h.flags |= CAM_UNLOCKED; | ccb->ccb_h.flags |= CAM_UNLOCKED; | ||||
cam_iosched_submit_trim(softc->cam_iosched); | |||||
} | } | ||||
static int | static int | ||||
cmd6workaround(union ccb *ccb) | cmd6workaround(union ccb *ccb) | ||||
{ | { | ||||
struct scsi_rw_6 cmd6; | struct scsi_rw_6 cmd6; | ||||
struct scsi_rw_10 *cmd10; | struct scsi_rw_10 *cmd10; | ||||
struct da_softc *softc; | struct da_softc *softc; | ||||
Show All 27 Lines | if (softc->delete_method == DA_DELETE_DISABLE) | ||||
da_delete_method_desc[old_method]); | da_delete_method_desc[old_method]); | ||||
else | else | ||||
xpt_print(ccb->ccb_h.path, | xpt_print(ccb->ccb_h.path, | ||||
"%s failed, switching to %s BIO_DELETE\n", | "%s failed, switching to %s BIO_DELETE\n", | ||||
da_delete_method_desc[old_method], | da_delete_method_desc[old_method], | ||||
da_delete_method_desc[softc->delete_method]); | da_delete_method_desc[softc->delete_method]); | ||||
while ((bp = bioq_takefirst(&softc->delete_run_queue)) != NULL) | while ((bp = bioq_takefirst(&softc->delete_run_queue)) != NULL) | ||||
bioq_disksort(&softc->delete_queue, bp); | cam_iosched_queue_work(softc->cam_iosched, bp); | ||||
bioq_disksort(&softc->delete_queue, | cam_iosched_queue_work(softc->cam_iosched, | ||||
(struct bio *)ccb->ccb_h.ccb_bp); | (struct bio *)ccb->ccb_h.ccb_bp); | ||||
ccb->ccb_h.ccb_bp = NULL; | ccb->ccb_h.ccb_bp = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Detect unsupported PREVENT ALLOW MEDIUM REMOVAL. */ | /* Detect unsupported PREVENT ALLOW MEDIUM REMOVAL. */ | ||||
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) == 0 && | if ((ccb->ccb_h.flags & CAM_CDB_POINTER) == 0 && | ||||
(*cdb == PREVENT_ALLOW) && | (*cdb == PREVENT_ALLOW) && | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | ||||
*/ | */ | ||||
/* | /* | ||||
* XXX See if this is really a media | * XXX See if this is really a media | ||||
* XXX change first? | * XXX change first? | ||||
*/ | */ | ||||
xpt_print(periph->path, | xpt_print(periph->path, | ||||
"Invalidating pack\n"); | "Invalidating pack\n"); | ||||
softc->flags |= DA_FLAG_PACK_INVALID; | softc->flags |= DA_FLAG_PACK_INVALID; | ||||
#ifdef CAM_IO_STATS | |||||
softc->invalidations++; | |||||
#endif | |||||
queued_error = ENXIO; | queued_error = ENXIO; | ||||
} | } | ||||
bioq_flush(&softc->bio_queue, NULL, | cam_iosched_flush(softc->cam_iosched, NULL, | ||||
queued_error); | queued_error); | ||||
if (bp != NULL) { | if (bp != NULL) { | ||||
bp->bio_error = error; | bp->bio_error = error; | ||||
bp->bio_resid = bp->bio_bcount; | bp->bio_resid = bp->bio_bcount; | ||||
bp->bio_flags |= BIO_ERROR; | bp->bio_flags |= BIO_ERROR; | ||||
} | } | ||||
} else if (bp != NULL) { | } else if (bp != NULL) { | ||||
if (state == DA_CCB_DELETE) | if (state == DA_CCB_DELETE) | ||||
Show All 26 Lines | #endif | ||||
softc->error_inject = 0; | softc->error_inject = 0; | ||||
} | } | ||||
} | } | ||||
LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); | LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); | ||||
if (LIST_EMPTY(&softc->pending_ccbs)) | if (LIST_EMPTY(&softc->pending_ccbs)) | ||||
softc->flags |= DA_FLAG_WAS_OTAG; | softc->flags |= DA_FLAG_WAS_OTAG; | ||||
cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb); | |||||
xpt_release_ccb(done_ccb); | xpt_release_ccb(done_ccb); | ||||
if (state == DA_CCB_DELETE) { | if (state == DA_CCB_DELETE) { | ||||
TAILQ_HEAD(, bio) queue; | TAILQ_HEAD(, bio) queue; | ||||
TAILQ_INIT(&queue); | TAILQ_INIT(&queue); | ||||
TAILQ_CONCAT(&queue, &softc->delete_run_queue.queue, bio_queue); | TAILQ_CONCAT(&queue, &softc->delete_run_queue.queue, bio_queue); | ||||
softc->delete_run_queue.insert_point = NULL; | softc->delete_run_queue.insert_point = NULL; | ||||
/* | /* | ||||
* Normally, the xpt_release_ccb() above would make sure | * Normally, the xpt_release_ccb() above would make sure | ||||
* that when we have more work to do, that work would | * that when we have more work to do, that work would | ||||
* get kicked off. However, we specifically keep | * get kicked off. However, we specifically keep | ||||
* delete_running set to 0 before the call above to | * delete_running set to 0 before the call above to | ||||
* allow other I/O to progress when many BIO_DELETE | * allow other I/O to progress when many BIO_DELETE | ||||
* requests are pushed down. We set delete_running to 0 | * requests are pushed down. We set delete_running to 0 | ||||
* and call daschedule again so that we don't stall if | * and call daschedule again so that we don't stall if | ||||
* there are no other I/Os pending apart from BIO_DELETEs. | * there are no other I/Os pending apart from BIO_DELETEs. | ||||
*/ | */ | ||||
softc->delete_running = 0; | cam_iosched_trim_done(softc->cam_iosched); | ||||
daschedule(periph); | daschedule(periph); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { | while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { | ||||
TAILQ_REMOVE(&queue, bp1, bio_queue); | TAILQ_REMOVE(&queue, bp1, bio_queue); | ||||
bp1->bio_error = bp->bio_error; | bp1->bio_error = bp->bio_error; | ||||
if (bp->bio_flags & BIO_ERROR) { | if (bp->bio_flags & BIO_ERROR) { | ||||
bp1->bio_flags |= BIO_ERROR; | bp1->bio_flags |= BIO_ERROR; | ||||
bp1->bio_resid = bp1->bio_bcount; | bp1->bio_resid = bp1->bio_bcount; | ||||
} else | } else | ||||
bp1->bio_resid = 0; | bp1->bio_resid = 0; | ||||
biodone(bp1); | biodone(bp1); | ||||
} | } | ||||
} else | } else { | ||||
daschedule(periph); | |||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
} | |||||
if (bp != NULL) | if (bp != NULL) | ||||
biodone(bp); | biodone(bp); | ||||
return; | return; | ||||
} | } | ||||
case DA_CCB_PROBE_RC: | case DA_CCB_PROBE_RC: | ||||
case DA_CCB_PROBE_RC16: | case DA_CCB_PROBE_RC16: | ||||
{ | { | ||||
struct scsi_read_capacity_data *rdcap; | struct scsi_read_capacity_data *rdcap; | ||||
▲ Show 20 Lines • Show All 368 Lines • ▼ Show 20 Lines | if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { | ||||
* by default. | * by default. | ||||
*/ | */ | ||||
u_int16_t old_rate = softc->disk->d_rotation_rate; | u_int16_t old_rate = softc->disk->d_rotation_rate; | ||||
softc->disk->d_rotation_rate = | softc->disk->d_rotation_rate = | ||||
scsi_2btoul(bdc->medium_rotation_rate); | scsi_2btoul(bdc->medium_rotation_rate); | ||||
if (softc->disk->d_rotation_rate == | if (softc->disk->d_rotation_rate == | ||||
SVPD_BDC_RATE_NON_ROTATING) { | SVPD_BDC_RATE_NON_ROTATING) { | ||||
softc->sort_io_queue = 0; | cam_iosched_set_sort_queue(softc->cam_iosched, 0); | ||||
softc->rotating = 0; | |||||
} | } | ||||
if (softc->disk->d_rotation_rate != old_rate) { | if (softc->disk->d_rotation_rate != old_rate) { | ||||
disk_attr_changed(softc->disk, | disk_attr_changed(softc->disk, | ||||
"GEOM::rotation_rate", M_NOWAIT); | "GEOM::rotation_rate", M_NOWAIT); | ||||
} | } | ||||
} else { | } else { | ||||
int error; | int error; | ||||
error = daerror(done_ccb, CAM_RETRY_SELTO, | error = daerror(done_ccb, CAM_RETRY_SELTO, | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { | ||||
* Disable queue sorting for non-rotational media | * Disable queue sorting for non-rotational media | ||||
* by default. | * by default. | ||||
*/ | */ | ||||
old_rate = softc->disk->d_rotation_rate; | old_rate = softc->disk->d_rotation_rate; | ||||
softc->disk->d_rotation_rate = | softc->disk->d_rotation_rate = | ||||
ata_params->media_rotation_rate; | ata_params->media_rotation_rate; | ||||
if (softc->disk->d_rotation_rate == | if (softc->disk->d_rotation_rate == | ||||
ATA_RATE_NON_ROTATING) { | ATA_RATE_NON_ROTATING) { | ||||
softc->sort_io_queue = 0; | cam_iosched_set_sort_queue(softc->cam_iosched, 0); | ||||
softc->rotating = 0; | |||||
} | } | ||||
if (softc->disk->d_rotation_rate != old_rate) { | if (softc->disk->d_rotation_rate != old_rate) { | ||||
disk_attr_changed(softc->disk, | disk_attr_changed(softc->disk, | ||||
"GEOM::rotation_rate", M_NOWAIT); | "GEOM::rotation_rate", M_NOWAIT); | ||||
} | } | ||||
} else { | } else { | ||||
int error; | int error; | ||||
error = daerror(done_ccb, CAM_RETRY_SELTO, | error = daerror(done_ccb, CAM_RETRY_SELTO, | ||||
SF_RETRY_UA|SF_NO_PRINT); | SF_RETRY_UA|SF_NO_PRINT); | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | else if (sense_key == SSD_KEY_UNIT_ATTENTION && | ||||
asc == 0x3a && (softc->flags & DA_FLAG_PACK_INVALID) == 0) { | asc == 0x3a && (softc->flags & DA_FLAG_PACK_INVALID) == 0) { | ||||
softc->flags |= DA_FLAG_PACK_INVALID; | softc->flags |= DA_FLAG_PACK_INVALID; | ||||
disk_media_gone(softc->disk, M_NOWAIT); | disk_media_gone(softc->disk, M_NOWAIT); | ||||
} | } | ||||
} | } | ||||
if (error == ERESTART) | if (error == ERESTART) | ||||
return (ERESTART); | return (ERESTART); | ||||
switch (ccb->ccb_h.status & CAM_STATUS_MASK) { | |||||
case CAM_CMD_TIMEOUT: | |||||
#ifdef CAM_IO_STATS | |||||
softc->timeouts++; | |||||
#endif | |||||
break; | |||||
case CAM_REQ_ABORTED: | |||||
case CAM_REQ_CMP_ERR: | |||||
case CAM_REQ_TERMIO: | |||||
case CAM_UNREC_HBA_ERROR: | |||||
case CAM_DATA_RUN_ERR: | |||||
#ifdef CAM_IO_STATS | |||||
softc->errors++; | |||||
#endif | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
/* | /* | ||||
* XXX | * XXX | ||||
* Until we have a better way of doing pack validation, | * Until we have a better way of doing pack validation, | ||||
* don't treat UAs as errors. | * don't treat UAs as errors. | ||||
*/ | */ | ||||
sense_flags |= SF_RETRY_UA; | sense_flags |= SF_RETRY_UA; | ||||
if (softc->quirks & DA_Q_RETRY_BUSY) | if (softc->quirks & DA_Q_RETRY_BUSY) | ||||
sense_flags |= SF_RETRY_BUSY; | sense_flags |= SF_RETRY_BUSY; | ||||
return(cam_periph_error(ccb, cam_flags, sense_flags, | return(cam_periph_error(ccb, cam_flags, sense_flags, | ||||
&softc->saved_ccb)); | &softc->saved_ccb)); | ||||
} | } | ||||
static void | static void | ||||
damediapoll(void *arg) | damediapoll(void *arg) | ||||
{ | { | ||||
struct cam_periph *periph = arg; | struct cam_periph *periph = arg; | ||||
struct da_softc *softc = periph->softc; | struct da_softc *softc = periph->softc; | ||||
if (!softc->tur && LIST_EMPTY(&softc->pending_ccbs)) { | if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR) && | ||||
LIST_EMPTY(&softc->pending_ccbs)) { | |||||
if (cam_periph_acquire(periph) == CAM_REQ_CMP) { | if (cam_periph_acquire(periph) == CAM_REQ_CMP) { | ||||
softc->tur = 1; | cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); | ||||
daschedule(periph); | daschedule(periph); | ||||
} | } | ||||
} | } | ||||
/* Queue us up again */ | /* Queue us up again */ | ||||
if (da_poll_period != 0) | if (da_poll_period != 0) | ||||
callout_schedule(&softc->mediapoll_c, da_poll_period * hz); | callout_schedule(&softc->mediapoll_c, da_poll_period * hz); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 341 Lines • Show Last 20 Lines |
XXX comment?