Changeset View
Changeset View
Standalone View
Standalone View
head/sys/cam/ata/ata_da.c
Show First 20 Lines • Show All 53 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/ata/ata_all.h> | #include <cam/ata/ata_all.h> | ||||
#include <machine/md_var.h> /* geometry translation */ | #include <machine/md_var.h> /* geometry translation */ | ||||
#ifdef _KERNEL | #ifdef _KERNEL | ||||
#define ATA_MAX_28BIT_LBA 268435455UL | #define ATA_MAX_28BIT_LBA 268435455UL | ||||
extern int iosched_debug; | |||||
typedef enum { | typedef enum { | ||||
ADA_STATE_RAHEAD, | ADA_STATE_RAHEAD, | ||||
ADA_STATE_WCACHE, | ADA_STATE_WCACHE, | ||||
ADA_STATE_NORMAL | ADA_STATE_NORMAL | ||||
} ada_state; | } ada_state; | ||||
typedef enum { | typedef enum { | ||||
ADA_FLAG_CAN_48BIT = 0x0002, | ADA_FLAG_CAN_48BIT = 0x0002, | ||||
ADA_FLAG_CAN_FLUSHCACHE = 0x0004, | ADA_FLAG_CAN_FLUSHCACHE = 0x0004, | ||||
ADA_FLAG_CAN_NCQ = 0x0008, | ADA_FLAG_CAN_NCQ = 0x0008, | ||||
ADA_FLAG_CAN_DMA = 0x0010, | ADA_FLAG_CAN_DMA = 0x0010, | ||||
ADA_FLAG_NEED_OTAG = 0x0020, | ADA_FLAG_NEED_OTAG = 0x0020, | ||||
ADA_FLAG_WAS_OTAG = 0x0040, | ADA_FLAG_WAS_OTAG = 0x0040, | ||||
ADA_FLAG_CAN_TRIM = 0x0080, | ADA_FLAG_CAN_TRIM = 0x0080, | ||||
ADA_FLAG_OPEN = 0x0100, | ADA_FLAG_OPEN = 0x0100, | ||||
ADA_FLAG_SCTX_INIT = 0x0200, | ADA_FLAG_SCTX_INIT = 0x0200, | ||||
ADA_FLAG_CAN_CFA = 0x0400, | ADA_FLAG_CAN_CFA = 0x0400, | ||||
ADA_FLAG_CAN_POWERMGT = 0x0800, | ADA_FLAG_CAN_POWERMGT = 0x0800, | ||||
ADA_FLAG_CAN_DMA48 = 0x1000, | ADA_FLAG_CAN_DMA48 = 0x1000, | ||||
ADA_FLAG_DIRTY = 0x2000 | ADA_FLAG_DIRTY = 0x2000, | ||||
ADA_FLAG_CAN_NCQ_TRIM = 0x4000, /* CAN_TRIM also set */ | |||||
ADA_FLAG_PIM_CAN_NCQ_TRIM = 0x8000 | |||||
} ada_flags; | } ada_flags; | ||||
typedef enum { | typedef enum { | ||||
ADA_Q_NONE = 0x00, | ADA_Q_NONE = 0x00, | ||||
ADA_Q_4K = 0x01, | ADA_Q_4K = 0x01, | ||||
ADA_Q_NCQ_TRIM_BROKEN = 0x02, | |||||
} ada_quirks; | } ada_quirks; | ||||
#define ADA_Q_BIT_STRING \ | #define ADA_Q_BIT_STRING \ | ||||
"\020" \ | "\020" \ | ||||
"\0014K" | "\0014K" \ | ||||
"\002NCQ_TRIM_BROKEN" | |||||
typedef enum { | typedef enum { | ||||
ADA_CCB_RAHEAD = 0x01, | ADA_CCB_RAHEAD = 0x01, | ||||
ADA_CCB_WCACHE = 0x02, | ADA_CCB_WCACHE = 0x02, | ||||
ADA_CCB_BUFFER_IO = 0x03, | ADA_CCB_BUFFER_IO = 0x03, | ||||
ADA_CCB_DUMP = 0x05, | ADA_CCB_DUMP = 0x05, | ||||
ADA_CCB_TRIM = 0x06, | ADA_CCB_TRIM = 0x06, | ||||
ADA_CCB_TYPE_MASK = 0x0F, | ADA_CCB_TYPE_MASK = 0x0F, | ||||
} ada_ccb_state; | } ada_ccb_state; | ||||
/* Offsets into our private area for storing information */ | /* Offsets into our private area for storing information */ | ||||
#define ccb_state ppriv_field0 | #define ccb_state ppriv_field0 | ||||
#define ccb_bp ppriv_ptr1 | #define ccb_bp ppriv_ptr1 | ||||
typedef enum { | |||||
ADA_DELETE_NONE, | |||||
ADA_DELETE_DISABLE, | |||||
ADA_DELETE_CFA_ERASE, | |||||
ADA_DELETE_DSM_TRIM, | |||||
ADA_DELETE_NCQ_DSM_TRIM, | |||||
ADA_DELETE_MIN = ADA_DELETE_CFA_ERASE, | |||||
ADA_DELETE_MAX = ADA_DELETE_NCQ_DSM_TRIM, | |||||
} ada_delete_methods; | |||||
static const char *ada_delete_method_names[] = | |||||
{ "NONE", "DISABLE", "CFA_ERASE", "DSM_TRIM", "NCQ_DSM_TRIM" }; | |||||
#if 0 | |||||
static const char *ada_delete_method_desc[] = | |||||
{ "NONE", "DISABLED", "CFA Erase", "DSM Trim", "DSM Trim via NCQ" }; | |||||
#endif | |||||
struct disk_params { | struct disk_params { | ||||
u_int8_t heads; | u_int8_t heads; | ||||
u_int8_t secs_per_track; | u_int8_t secs_per_track; | ||||
u_int32_t cylinders; | u_int32_t cylinders; | ||||
u_int32_t secsize; /* Number of bytes/logical sector */ | u_int32_t secsize; /* Number of bytes/logical sector */ | ||||
u_int64_t sectors; /* Total number sectors */ | u_int64_t sectors; /* Total number sectors */ | ||||
}; | }; | ||||
#define TRIM_MAX_BLOCKS 8 | #define TRIM_MAX_BLOCKS 8 | ||||
#define TRIM_MAX_RANGES (TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES) | #define TRIM_MAX_RANGES (TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES) | ||||
struct trim_request { | struct trim_request { | ||||
uint8_t data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE]; | uint8_t data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE]; | ||||
TAILQ_HEAD(, bio) bps; | TAILQ_HEAD(, bio) bps; | ||||
}; | }; | ||||
struct ada_softc { | struct ada_softc { | ||||
struct bio_queue_head bio_queue; | struct cam_iosched_softc *cam_iosched; | ||||
struct bio_queue_head trim_queue; | |||||
int outstanding_cmds; /* Number of active commands */ | int outstanding_cmds; /* Number of active commands */ | ||||
int refcount; /* Active xpt_action() calls */ | int refcount; /* Active xpt_action() calls */ | ||||
ada_state state; | ada_state state; | ||||
ada_flags flags; | ada_flags flags; | ||||
ada_quirks quirks; | ada_quirks quirks; | ||||
int sort_io_queue; | ada_delete_methods delete_method; | ||||
int trim_max_ranges; | int trim_max_ranges; | ||||
int trim_running; | |||||
int read_ahead; | int read_ahead; | ||||
int write_cache; | int write_cache; | ||||
int unmappedio; | |||||
int rotating; | |||||
#ifdef ADA_TEST_FAILURE | #ifdef ADA_TEST_FAILURE | ||||
int force_read_error; | int force_read_error; | ||||
int force_write_error; | int force_write_error; | ||||
int periodic_read_error; | int periodic_read_error; | ||||
int periodic_read_count; | int periodic_read_count; | ||||
#endif | #endif | ||||
struct disk_params params; | struct disk_params params; | ||||
struct disk *disk; | struct disk *disk; | ||||
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; | ||||
struct trim_request trim_req; | struct trim_request trim_req; | ||||
#ifdef CAM_IO_STATS | |||||
struct sysctl_ctx_list sysctl_stats_ctx; | |||||
struct sysctl_oid *sysctl_stats_tree; | |||||
u_int timeouts; | |||||
u_int errors; | |||||
u_int invalidations; | |||||
#endif | |||||
}; | }; | ||||
struct ada_quirk_entry { | struct ada_quirk_entry { | ||||
struct scsi_inquiry_pattern inq_pat; | struct scsi_inquiry_pattern inq_pat; | ||||
ada_quirks quirks; | ada_quirks quirks; | ||||
}; | }; | ||||
static struct ada_quirk_entry ada_quirk_table[] = | static struct ada_quirk_entry ada_quirk_table[] = | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | /* SSDs */ | ||||
* Crucial M4 SSDs | * Crucial M4 SSDs | ||||
* 4k optimised & trim only works in 4k requests + 4k aligned | * 4k optimised & trim only works in 4k requests + 4k aligned | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "M4-CT???M4SSD2*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "M4-CT???M4SSD2*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Crucial M500 SSDs EU07 firmware | |||||
* NCQ Trim works ? | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "EU07" }, | |||||
/*quirks*/0 | |||||
}, | |||||
{ | |||||
/* | |||||
* Crucial M500 SSDs all other firmware | |||||
* NCQ Trim doesn't work | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "*" }, | |||||
/*quirks*/ADA_Q_NCQ_TRIM_BROKEN | |||||
}, | |||||
{ | |||||
/* | |||||
* Crucial M550 SSDs | |||||
* NCQ Trim doesn't work, but only on MU01 firmware | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M550*", "MU01" }, | |||||
/*quirks*/ADA_Q_NCQ_TRIM_BROKEN | |||||
}, | |||||
{ | |||||
/* | |||||
* Crucial MX100 SSDs | |||||
* NCQ Trim doesn't work, but only on MU01 firmware | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*MX100*", "MU01" }, | |||||
/*quirks*/ADA_Q_NCQ_TRIM_BROKEN | |||||
}, | |||||
{ | |||||
/* | |||||
* Crucial RealSSD C300 SSDs | * Crucial RealSSD C300 SSDs | ||||
* 4k optimised | * 4k optimised | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*", | { T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*", | ||||
"*" }, /*quirks*/ADA_Q_4K | "*" }, /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | /* SSDs */ | ||||
* Marvell SSDs (entry taken from OpenSolaris) | * Marvell SSDs (entry taken from OpenSolaris) | ||||
* 4k optimised & trim only works in 4k requests + 4k aligned | * 4k optimised & trim only works in 4k requests + 4k aligned | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "MARVELL SD88SA02*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "MARVELL SD88SA02*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Micron M500 SSDs firmware EU07 | |||||
* NCQ Trim works? | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "EU07" }, | |||||
/*quirks*/0 | |||||
}, | |||||
{ | |||||
/* | |||||
* Micron M500 SSDs all other firmware | |||||
* NCQ Trim doesn't work | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "*" }, | |||||
/*quirks*/ADA_Q_NCQ_TRIM_BROKEN | |||||
}, | |||||
{ | |||||
/* | |||||
* Micron M5[15]0 SSDs | |||||
* NCQ Trim doesn't work, but only MU01 firmware | |||||
*/ | |||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M5[15]0*", "MU01" }, | |||||
/*quirks*/ADA_Q_NCQ_TRIM_BROKEN | |||||
}, | |||||
{ | |||||
/* | |||||
* OCZ Agility 2 SSDs | * OCZ Agility 2 SSDs | ||||
* 4k optimised & trim only works in 4k requests + 4k aligned | * 4k optimised & trim only works in 4k requests + 4k aligned | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
Show All 33 Lines | /* SSDs */ | ||||
* 4k optimised & trim only works in 4k requests + 4k aligned | * 4k optimised & trim only works in 4k requests + 4k aligned | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Samsung 830 Series SSDs | * Samsung 830 Series SSDs | ||||
* 4k optimised | * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Samsung 840 SSDs | * Samsung 840 SSDs | ||||
* 4k optimised | * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Samsung 850 SSDs | * Samsung 850 SSDs | ||||
* 4k optimised | * 4k optimised, NCQ TRIM broken (normal TRIM fine) | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* Samsung 843T Series SSDs (MZ7WD*) | * Samsung 843T Series SSDs (MZ7WD*) | ||||
* Samsung PM851 Series SSDs (MZ7TE*) | * Samsung PM851 Series SSDs (MZ7TE*) | ||||
* Samsung PM853T Series SSDs (MZ7GE*) | * Samsung PM853T Series SSDs (MZ7GE*) | ||||
* Samsung SM863 Series SSDs (MZ7KM*) | * Samsung SM863 Series SSDs (MZ7KM*) | ||||
* 4k optimised | * 4k optimised, NCQ Trim believed working | ||||
*/ | */ | ||||
{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" }, | { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" }, | ||||
/*quirks*/ADA_Q_4K | /*quirks*/ADA_Q_4K | ||||
}, | }, | ||||
{ | { | ||||
/* | /* | ||||
* SuperTalent TeraDrive CT SSDs | * SuperTalent TeraDrive CT SSDs | ||||
* 4k optimised & trim only works in 4k requests + 4k aligned | * 4k optimised & trim only works in 4k requests + 4k aligned | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
#ifndef ADA_DEFAULT_WRITE_CACHE | #ifndef ADA_DEFAULT_WRITE_CACHE | ||||
#define ADA_DEFAULT_WRITE_CACHE 1 | #define ADA_DEFAULT_WRITE_CACHE 1 | ||||
#endif | #endif | ||||
#define ADA_RA (softc->read_ahead >= 0 ? \ | #define ADA_RA (softc->read_ahead >= 0 ? \ | ||||
softc->read_ahead : ada_read_ahead) | softc->read_ahead : ada_read_ahead) | ||||
#define ADA_WC (softc->write_cache >= 0 ? \ | #define ADA_WC (softc->write_cache >= 0 ? \ | ||||
softc->write_cache : ada_write_cache) | softc->write_cache : ada_write_cache) | ||||
#define ADA_SIO (softc->sort_io_queue >= 0 ? \ | |||||
softc->sort_io_queue : cam_sort_io_queues) | |||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
#ifndef ata_disk_firmware_geom_adjust | #ifndef ata_disk_firmware_geom_adjust | ||||
#define ata_disk_firmware_geom_adjust(disk) | #define ata_disk_firmware_geom_adjust(disk) | ||||
#endif | #endif | ||||
Show All 40 Lines | |||||
#endif | #endif | ||||
static struct periph_driver adadriver = | static struct periph_driver adadriver = | ||||
{ | { | ||||
adainit, "ada", | adainit, "ada", | ||||
TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0 | TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0 | ||||
}; | }; | ||||
static int adadeletemethodsysctl(SYSCTL_HANDLER_ARGS); | |||||
PERIPHDRIVER_DECLARE(ada, adadriver); | PERIPHDRIVER_DECLARE(ada, adadriver); | ||||
static int | static int | ||||
adaopen(struct disk *dp) | adaopen(struct disk *dp) | ||||
{ | { | ||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct ada_softc *softc; | struct ada_softc *softc; | ||||
int error; | int error; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
adaschedule(struct cam_periph *periph) | adaschedule(struct cam_periph *periph) | ||||
{ | { | ||||
struct ada_softc *softc = (struct ada_softc *)periph->softc; | struct ada_softc *softc = (struct ada_softc *)periph->softc; | ||||
if (softc->state != ADA_STATE_NORMAL) | if (softc->state != ADA_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->trim_running && bioq_first(&softc->trim_queue))) { | |||||
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 | ||||
adastrategy(struct bio *bp) | adastrategy(struct bio *bp) | ||||
Show All 15 Lines | if ((periph->flags & CAM_PERIPH_INVALID) != 0) { | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
biofinish(bp, NULL, ENXIO); | biofinish(bp, NULL, ENXIO); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* 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->trim_queue, bp); | |||||
} else { | |||||
if (ADA_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. | ||||
*/ | */ | ||||
adaschedule(periph); | adaschedule(periph); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) { | ||||
ccb.ccb_h.ccb_state = ADA_CCB_DUMP; | ccb.ccb_h.ccb_state = ADA_CCB_DUMP; | ||||
cam_fill_ataio(&ccb.ataio, | cam_fill_ataio(&ccb.ataio, | ||||
0, | 0, | ||||
adadone, | adadone, | ||||
CAM_DIR_NONE, | CAM_DIR_NONE, | ||||
0, | 0, | ||||
NULL, | NULL, | ||||
0, | 0, | ||||
ada_default_timeout*1000); | 5*1000); | ||||
ngieUnsubmitted Not Done Inline Actions
ngie: - Why 5?
- Why get rid of the parameter? | |||||
impAuthorUnsubmitted Not Done Inline ActionsThat's a regression. It used to be 5 seconds :) imp: That's a regression. It used to be 5 seconds :) | |||||
if (softc->flags & ADA_FLAG_CAN_48BIT) | if (softc->flags & ADA_FLAG_CAN_48BIT) | ||||
ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0); | ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0); | ||||
else | else | ||||
ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0); | ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0); | ||||
xpt_polled_action(&ccb); | xpt_polled_action(&ccb); | ||||
error = cam_periph_error(&ccb, | error = cam_periph_error(&ccb, | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | adaoninvalidate(struct cam_periph *periph) | ||||
struct ada_softc *softc; | struct ada_softc *softc; | ||||
softc = (struct ada_softc *)periph->softc; | softc = (struct ada_softc *)periph->softc; | ||||
/* | /* | ||||
* De-register any async callbacks. | * De-register any async callbacks. | ||||
*/ | */ | ||||
xpt_register_async(0, adaasync, periph, periph->path); | xpt_register_async(0, adaasync, periph, periph->path); | ||||
#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->trim_queue, NULL, ENXIO); | |||||
disk_gone(softc->disk); | disk_gone(softc->disk); | ||||
} | } | ||||
static void | static void | ||||
adacleanup(struct cam_periph *periph) | adacleanup(struct cam_periph *periph) | ||||
{ | { | ||||
struct ada_softc *softc; | struct ada_softc *softc; | ||||
softc = (struct ada_softc *)periph->softc; | softc = (struct ada_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 & ADA_FLAG_SCTX_INIT) != 0 | if ((softc->flags & ADA_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"); | |||||
} | } | ||||
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); | ||||
} | } | ||||
static void | static void | ||||
adasetdeletemethod(struct ada_softc *softc) | |||||
{ | |||||
if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) | |||||
softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM; | |||||
else if (softc->flags & ADA_FLAG_CAN_TRIM) | |||||
softc->delete_method = ADA_DELETE_DSM_TRIM; | |||||
else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT)) | |||||
softc->delete_method = ADA_DELETE_CFA_ERASE; | |||||
else | |||||
softc->delete_method = ADA_DELETE_NONE; | |||||
} | |||||
static void | |||||
adaasync(void *callback_arg, u_int32_t code, | adaasync(void *callback_arg, u_int32_t code, | ||||
struct cam_path *path, void *arg) | struct cam_path *path, void *arg) | ||||
{ | { | ||||
struct ccb_getdev cgd; | struct ccb_getdev cgd; | ||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct ada_softc *softc; | struct ada_softc *softc; | ||||
periph = (struct cam_periph *)callback_arg; | periph = (struct cam_periph *)callback_arg; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | case AC_GETDEV_CHANGED: | ||||
} else | } else | ||||
softc->flags &= ~(ADA_FLAG_CAN_48BIT | | softc->flags &= ~(ADA_FLAG_CAN_48BIT | | ||||
ADA_FLAG_CAN_DMA48); | ADA_FLAG_CAN_DMA48); | ||||
if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) && | if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) && | ||||
(cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue)) | (cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue)) | ||||
softc->flags |= ADA_FLAG_CAN_NCQ; | softc->flags |= ADA_FLAG_CAN_NCQ; | ||||
else | else | ||||
softc->flags &= ~ADA_FLAG_CAN_NCQ; | softc->flags &= ~ADA_FLAG_CAN_NCQ; | ||||
if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) && | if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) && | ||||
(cgd.inq_flags & SID_DMA)) | (cgd.inq_flags & SID_DMA)) { | ||||
softc->flags |= ADA_FLAG_CAN_TRIM; | softc->flags |= ADA_FLAG_CAN_TRIM; | ||||
/* | |||||
* If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do | |||||
* NCQ trims, if we support trims at all. We also need support from | |||||
* the sim do do things properly. Perhaps we should look at log 13 | |||||
* dword 0 bit 0 and dword 1 bit 0 are set too... | |||||
*/ | |||||
if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && | |||||
(softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 && | |||||
(cgd.ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && | |||||
(softc->flags & ADA_FLAG_CAN_TRIM) != 0) | |||||
softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; | |||||
else | else | ||||
softc->flags &= ~ADA_FLAG_CAN_TRIM; | softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; | ||||
} else | |||||
softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM); | |||||
adasetdeletemethod(softc); | |||||
cam_periph_async(periph, code, path, arg); | cam_periph_async(periph, code, path, arg); | ||||
break; | break; | ||||
} | } | ||||
case AC_ADVINFO_CHANGED: | case AC_ADVINFO_CHANGED: | ||||
{ | { | ||||
uintptr_t buftype; | uintptr_t buftype; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, | ||||
SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2, | SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2, | ||||
CTLFLAG_RD, 0, tmpstr); | CTLFLAG_RD, 0, tmpstr); | ||||
if (softc->sysctl_tree == NULL) { | if (softc->sysctl_tree == NULL) { | ||||
printf("adasysctlinit: unable to allocate sysctl tree\n"); | printf("adasysctlinit: unable to allocate sysctl tree\n"); | ||||
cam_periph_release(periph); | cam_periph_release(periph); | ||||
return; | return; | ||||
} | } | ||||
SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | |||||
OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW, | |||||
softc, 0, adadeletemethodsysctl, "A", | |||||
"BIO_DELETE execution method"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
&softc->read_ahead, 0, "Enable disk read ahead."); | &softc->read_ahead, 0, "Enable disk read ahead."); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
&softc->write_cache, 0, "Enable disk write cache."); | &softc->write_cache, 0, "Enable disk write cache."); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "sort_io_queue", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "unmapped_io", CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||
&softc->sort_io_queue, 0, | &softc->unmappedio, 0, "Unmapped I/O leaf"); | ||||
"Sort IO queue to try and optimise disk access patterns"); | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "rotating", CTLFLAG_RD | CTLFLAG_MPSAFE, | |||||
&softc->rotating, 0, "Rotating media"); | |||||
#ifdef ADA_TEST_FAILURE | #ifdef ADA_TEST_FAILURE | ||||
/* | /* | ||||
* Add a 'door bell' sysctl which allows one to set it from userland | * Add a 'door bell' sysctl which allows one to set it from userland | ||||
* and cause something bad to happen. For the moment, we only allow | * and cause something bad to happen. For the moment, we only allow | ||||
* whacking the next read or write. | * whacking the next read or write. | ||||
*/ | */ | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
&softc->force_read_error, 0, | &softc->force_read_error, 0, | ||||
"Force a read error for the next N reads."); | "Force a read error for the next N reads."); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
&softc->force_write_error, 0, | &softc->force_write_error, 0, | ||||
"Force a write error for the next N writes."); | "Force a write error for the next N writes."); | ||||
SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), | ||||
OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
&softc->periodic_read_error, 0, | &softc->periodic_read_error, 0, | ||||
"Force a read error every N reads (don't set too low)."); | "Force a read error every N reads (don't set too low)."); | ||||
#endif | #endif | ||||
#ifdef CAM_IO_STATS | |||||
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, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE, | |||||
&softc->timeouts, 0, | |||||
"Device timeouts reported by the SIM"); | |||||
SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_stats_tree), | |||||
OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE, | |||||
&softc->errors, 0, | |||||
"Transport errors reported by the SIM."); | |||||
SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, | |||||
SYSCTL_CHILDREN(softc->sysctl_stats_tree), | |||||
OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE, | |||||
&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 | ||||
adagetattr(struct bio *bp) | adagetattr(struct bio *bp) | ||||
{ | { | ||||
int ret; | int ret; | ||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
periph = (struct cam_periph *)bp->bio_disk->d_drv1; | periph = (struct cam_periph *)bp->bio_disk->d_drv1; | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, | ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, | ||||
periph->path); | periph->path); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
if (ret == 0) | if (ret == 0) | ||||
bp->bio_completed = bp->bio_length; | bp->bio_completed = bp->bio_length; | ||||
return ret; | return ret; | ||||
} | } | ||||
static int | |||||
adadeletemethodsysctl(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
char buf[16]; | |||||
ngieUnsubmitted Not Done Inline ActionsWhy 16? ngie: Why 16? | |||||
impAuthorUnsubmitted Not Done Inline ActionsBecause it's long enough and doesn't warrant its own #define. imp: Because it's long enough and doesn't warrant its own #define. | |||||
const char *p; | |||||
struct ada_softc *softc; | |||||
int i, error, value, methods; | |||||
softc = (struct ada_softc *)arg1; | |||||
value = softc->delete_method; | |||||
if (value < 0 || value > ADA_DELETE_MAX) | |||||
p = "UNKNOWN"; | |||||
else | |||||
p = ada_delete_method_names[value]; | |||||
strncpy(buf, p, sizeof(buf)); | |||||
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | |||||
if (error != 0 || req->newptr == NULL) | |||||
return (error); | |||||
methods = 1 << ADA_DELETE_DISABLE; | |||||
if ((softc->flags & ADA_FLAG_CAN_CFA) && | |||||
!(softc->flags & ADA_FLAG_CAN_48BIT)) | |||||
methods |= 1 << ADA_DELETE_CFA_ERASE; | |||||
if (softc->flags & ADA_FLAG_CAN_TRIM) | |||||
methods |= 1 << ADA_DELETE_DSM_TRIM; | |||||
if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) | |||||
methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM; | |||||
for (i = 0; i <= ADA_DELETE_MAX; i++) { | |||||
if (!(methods & (1 << i)) || | |||||
strcmp(buf, ada_delete_method_names[i]) != 0) | |||||
continue; | |||||
softc->delete_method = i; | |||||
return (0); | |||||
} | |||||
return (EINVAL); | |||||
} | |||||
static cam_status | static cam_status | ||||
adaregister(struct cam_periph *periph, void *arg) | adaregister(struct cam_periph *periph, void *arg) | ||||
{ | { | ||||
struct ada_softc *softc; | struct ada_softc *softc; | ||||
struct ccb_pathinq cpi; | struct ccb_pathinq cpi; | ||||
struct ccb_getdev *cgd; | struct ccb_getdev *cgd; | ||||
char announce_buf[80]; | char announce_buf[80]; | ||||
struct disk_params *dp; | struct disk_params *dp; | ||||
Show All 11 Lines | softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF, | ||||
M_NOWAIT|M_ZERO); | M_NOWAIT|M_ZERO); | ||||
if (softc == NULL) { | if (softc == NULL) { | ||||
printf("adaregister: Unable to probe new device. " | printf("adaregister: 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); | ||||
} | } | ||||
bioq_init(&softc->bio_queue); | if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { | ||||
bioq_init(&softc->trim_queue); | printf("adaregister: Unable to probe new device. " | ||||
"Unable to allocate iosched memory\n"); | |||||
return(CAM_REQ_CMP_ERR); | |||||
} | |||||
if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) && | if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) && | ||||
(cgd->inq_flags & SID_DMA)) | (cgd->inq_flags & SID_DMA)) | ||||
softc->flags |= ADA_FLAG_CAN_DMA; | softc->flags |= ADA_FLAG_CAN_DMA; | ||||
if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) { | if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) { | ||||
softc->flags |= ADA_FLAG_CAN_48BIT; | softc->flags |= ADA_FLAG_CAN_48BIT; | ||||
if (cgd->inq_flags & SID_DMA48) | if (cgd->inq_flags & SID_DMA48) | ||||
softc->flags |= ADA_FLAG_CAN_DMA48; | softc->flags |= ADA_FLAG_CAN_DMA48; | ||||
Show All 13 Lines | if (cgd->ident_data.max_dsm_blocks != 0) { | ||||
softc->trim_max_ranges = | softc->trim_max_ranges = | ||||
min(cgd->ident_data.max_dsm_blocks * | min(cgd->ident_data.max_dsm_blocks * | ||||
ATA_DSM_BLK_RANGES, softc->trim_max_ranges); | ATA_DSM_BLK_RANGES, softc->trim_max_ranges); | ||||
} | } | ||||
} | } | ||||
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; | ||||
adasetdeletemethod(softc); | |||||
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->ident_data, | match = cam_quirkmatch((caddr_t)&cgd->ident_data, | ||||
(caddr_t)ada_quirk_table, | (caddr_t)ada_quirk_table, | ||||
sizeof(ada_quirk_table)/sizeof(*ada_quirk_table), | sizeof(ada_quirk_table)/sizeof(*ada_quirk_table), | ||||
Show All 24 Lines | adaregister(struct cam_periph *periph, void *arg) | ||||
snprintf(announce_buf, sizeof(announce_buf), | snprintf(announce_buf, sizeof(announce_buf), | ||||
"kern.cam.ada.%d.read_ahead", periph->unit_number); | "kern.cam.ada.%d.read_ahead", periph->unit_number); | ||||
TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead); | TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead); | ||||
softc->write_cache = -1; | softc->write_cache = -1; | ||||
snprintf(announce_buf, sizeof(announce_buf), | snprintf(announce_buf, sizeof(announce_buf), | ||||
"kern.cam.ada.%d.write_cache", periph->unit_number); | "kern.cam.ada.%d.write_cache", periph->unit_number); | ||||
TUNABLE_INT_FETCH(announce_buf, &softc->write_cache); | TUNABLE_INT_FETCH(announce_buf, &softc->write_cache); | ||||
/* Disable queue sorting for non-rotational media by default. */ | /* Disable queue sorting for non-rotational media by default. */ | ||||
if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) | if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) { | ||||
softc->sort_io_queue = 0; | softc->rotating = 0; | ||||
else | } else { | ||||
softc->sort_io_queue = -1; | softc->rotating = 1; | ||||
} | |||||
cam_iosched_set_sort_queue(softc->cam_iosched, softc->rotating ? -1 : 0); | |||||
adagetparams(periph, cgd); | adagetparams(periph, cgd); | ||||
softc->disk = disk_alloc(); | softc->disk = disk_alloc(); | ||||
softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate; | softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate; | ||||
softc->disk->d_devstat = devstat_new_entry(periph->periph_name, | softc->disk->d_devstat = devstat_new_entry(periph->periph_name, | ||||
periph->unit_number, softc->params.secsize, | periph->unit_number, softc->params.secsize, | ||||
DEVSTAT_ALL_SUPPORTED, | DEVSTAT_ALL_SUPPORTED, | ||||
DEVSTAT_TYPE_DIRECT | | DEVSTAT_TYPE_DIRECT | | ||||
XPORT_DEVSTAT_TYPE(cpi.transport), | XPORT_DEVSTAT_TYPE(cpi.transport), | ||||
Show All 26 Lines | softc->disk->d_delmaxsize = softc->params.secsize * | ||||
ATA_DSM_RANGE_MAX * | ATA_DSM_RANGE_MAX * | ||||
softc->trim_max_ranges; | softc->trim_max_ranges; | ||||
} else if ((softc->flags & ADA_FLAG_CAN_CFA) && | } else if ((softc->flags & ADA_FLAG_CAN_CFA) && | ||||
!(softc->flags & ADA_FLAG_CAN_48BIT)) { | !(softc->flags & ADA_FLAG_CAN_48BIT)) { | ||||
softc->disk->d_flags |= DISKFLAG_CANDELETE; | softc->disk->d_flags |= DISKFLAG_CANDELETE; | ||||
softc->disk->d_delmaxsize = 256 * softc->params.secsize; | softc->disk->d_delmaxsize = 256 * softc->params.secsize; | ||||
} else | } else | ||||
softc->disk->d_delmaxsize = maxio; | softc->disk->d_delmaxsize = maxio; | ||||
if ((cpi.hba_misc & PIM_UNMAPPED) != 0) | if ((cpi.hba_misc & PIM_UNMAPPED) != 0) { | ||||
softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; | softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; | ||||
softc->unmappedio = 1; | |||||
} | |||||
/* | |||||
* If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do | |||||
* NCQ trims, if we support trims at all. We also need support from | |||||
* the sim do do things properly. Perhaps we should look at log 13 | |||||
* dword 0 bit 0 and dword 1 bit 0 are set too... | |||||
*/ | |||||
if (cpi.hba_misc & PIM_NCQ_KLUDGE) | |||||
softc->flags |= ADA_FLAG_PIM_CAN_NCQ_TRIM; | |||||
if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && | |||||
(softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 && | |||||
(cgd->ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && | |||||
(softc->flags & ADA_FLAG_CAN_TRIM) != 0) | |||||
softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; | |||||
strlcpy(softc->disk->d_descr, cgd->ident_data.model, | strlcpy(softc->disk->d_descr, cgd->ident_data.model, | ||||
MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model))); | MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model))); | ||||
strlcpy(softc->disk->d_ident, cgd->ident_data.serial, | strlcpy(softc->disk->d_ident, cgd->ident_data.serial, | ||||
MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial))); | MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial))); | ||||
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; | ||||
softc->disk->d_hba_subvendor = cpi.hba_subvendor; | softc->disk->d_hba_subvendor = cpi.hba_subvendor; | ||||
softc->disk->d_hba_subdevice = cpi.hba_subdevice; | softc->disk->d_hba_subdevice = cpi.hba_subdevice; | ||||
Show All 10 Lines | softc->disk->d_stripeoffset = (softc->disk->d_stripesize - | ||||
softc->disk->d_stripesize; | softc->disk->d_stripesize; | ||||
} else if (softc->quirks & ADA_Q_4K) { | } else if (softc->quirks & ADA_Q_4K) { | ||||
softc->disk->d_stripesize = 4096; | softc->disk->d_stripesize = 4096; | ||||
softc->disk->d_stripeoffset = 0; | softc->disk->d_stripeoffset = 0; | ||||
} | } | ||||
softc->disk->d_fwsectors = softc->params.secs_per_track; | softc->disk->d_fwsectors = softc->params.secs_per_track; | ||||
softc->disk->d_fwheads = softc->params.heads; | softc->disk->d_fwheads = softc->params.heads; | ||||
ata_disk_firmware_geom_adjust(softc->disk); | ata_disk_firmware_geom_adjust(softc->disk); | ||||
adasetdeletemethod(softc); | |||||
/* | /* | ||||
* Acquire a reference to the periph before we register with GEOM. | * Acquire a reference to the periph before we register with GEOM. | ||||
* We'll release this reference once GEOM calls us back (via | * We'll release this reference once GEOM calls us back (via | ||||
* adadiskgonecb()) telling us that our provider has been freed. | * adadiskgonecb()) telling us that our provider has been freed. | ||||
*/ | */ | ||||
if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | ||||
xpt_print(periph->path, "%s: lost periph during " | xpt_print(periph->path, "%s: lost periph during " | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | adaregister(struct cam_periph *periph, void *arg) | ||||
} | } | ||||
if (cam_periph_acquire(periph) != CAM_REQ_CMP) | if (cam_periph_acquire(periph) != CAM_REQ_CMP) | ||||
softc->state = ADA_STATE_NORMAL; | softc->state = ADA_STATE_NORMAL; | ||||
else | else | ||||
xpt_schedule(periph, CAM_PRIORITY_DEV); | xpt_schedule(periph, CAM_PRIORITY_DEV); | ||||
return(CAM_REQ_CMP); | return(CAM_REQ_CMP); | ||||
} | } | ||||
static void | static int | ||||
ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) | ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req) | ||||
{ | { | ||||
struct trim_request *req = &softc->trim_req; | |||||
uint64_t lastlba = (uint64_t)-1; | uint64_t lastlba = (uint64_t)-1; | ||||
int c, lastcount = 0, off, ranges = 0; | int c, lastcount = 0, off, ranges = 0; | ||||
bzero(req, sizeof(*req)); | bzero(req, sizeof(*req)); | ||||
TAILQ_INIT(&req->bps); | TAILQ_INIT(&req->bps); | ||||
do { | do { | ||||
uint64_t lba = bp->bio_pblkno; | uint64_t lba = bp->bio_pblkno; | ||||
int count = bp->bio_bcount / softc->params.secsize; | int count = bp->bio_bcount / softc->params.secsize; | ||||
bioq_remove(&softc->trim_queue, bp); | |||||
/* Try to extend the previous range. */ | /* Try to extend the previous range. */ | ||||
if (lba == lastlba) { | if (lba == lastlba) { | ||||
c = min(count, ATA_DSM_RANGE_MAX - lastcount); | c = min(count, ATA_DSM_RANGE_MAX - lastcount); | ||||
lastcount += c; | lastcount += c; | ||||
off = (ranges - 1) * ATA_DSM_RANGE_SIZE; | off = (ranges - 1) * ATA_DSM_RANGE_SIZE; | ||||
req->data[off + 6] = lastcount & 0xff; | req->data[off + 6] = lastcount & 0xff; | ||||
req->data[off + 7] = | req->data[off + 7] = | ||||
(lastcount >> 8) & 0xff; | (lastcount >> 8) & 0xff; | ||||
Show All 19 Lines | while (count > 0) { | ||||
/* | /* | ||||
* Its the caller's responsibility to ensure the | * Its the caller's responsibility to ensure the | ||||
* request will fit so we don't need to check for | * request will fit so we don't need to check for | ||||
* overrun here | * overrun here | ||||
*/ | */ | ||||
} | } | ||||
lastlba = lba; | lastlba = lba; | ||||
TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); | TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); | ||||
bp = bioq_first(&softc->trim_queue); | |||||
if (bp == NULL || | bp = cam_iosched_next_trim(softc->cam_iosched); | ||||
bp->bio_bcount / softc->params.secsize > | if (bp == NULL) | ||||
(softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) | |||||
break; | break; | ||||
if (bp->bio_bcount / softc->params.secsize > | |||||
(softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { | |||||
cam_iosched_put_back_trim(softc->cam_iosched, bp); | |||||
break; | |||||
} | |||||
} while (1); | } while (1); | ||||
return (ranges); | |||||
} | |||||
static void | |||||
ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) | |||||
{ | |||||
struct trim_request *req = &softc->trim_req; | |||||
int ranges; | |||||
ranges = ada_dsmtrim_req_create(softc, bp, req); | |||||
cam_fill_ataio(ataio, | cam_fill_ataio(ataio, | ||||
ada_retry_count, | ada_retry_count, | ||||
adadone, | adadone, | ||||
CAM_DIR_OUT, | CAM_DIR_OUT, | ||||
0, | 0, | ||||
req->data, | req->data, | ||||
((ranges + ATA_DSM_BLK_RANGES - 1) / | ((ranges + ATA_DSM_BLK_RANGES - 1) / | ||||
ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, | ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, | ||||
ada_default_timeout * 1000); | ada_default_timeout * 1000); | ||||
ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT, | ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT, | ||||
ATA_DSM_TRIM, 0, (ranges + ATA_DSM_BLK_RANGES - | ATA_DSM_TRIM, 0, (ranges + ATA_DSM_BLK_RANGES - | ||||
1) / ATA_DSM_BLK_RANGES); | 1) / ATA_DSM_BLK_RANGES); | ||||
} | } | ||||
static void | static void | ||||
ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) | |||||
{ | |||||
struct trim_request *req = &softc->trim_req; | |||||
int ranges; | |||||
ranges = ada_dsmtrim_req_create(softc, bp, req); | |||||
cam_fill_ataio(ataio, | |||||
ada_retry_count, | |||||
adadone, | |||||
CAM_DIR_OUT, | |||||
0, | |||||
req->data, | |||||
((ranges + ATA_DSM_BLK_RANGES - 1) / | |||||
ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, | |||||
ada_default_timeout * 1000); | |||||
ata_ncq_cmd(ataio, | |||||
ATA_SEND_FPDMA_QUEUED, | |||||
0, | |||||
(ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES); | |||||
ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM; | |||||
ataio->cmd.flags |= CAM_ATAIO_AUX_HACK; | |||||
} | |||||
static void | |||||
ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) | ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) | ||||
{ | { | ||||
struct trim_request *req = &softc->trim_req; | struct trim_request *req = &softc->trim_req; | ||||
uint64_t lba = bp->bio_pblkno; | uint64_t lba = bp->bio_pblkno; | ||||
uint16_t count = bp->bio_bcount / softc->params.secsize; | uint16_t count = bp->bio_bcount / softc->params.secsize; | ||||
bzero(req, sizeof(*req)); | bzero(req, sizeof(*req)); | ||||
TAILQ_INIT(&req->bps); | TAILQ_INIT(&req->bps); | ||||
bioq_remove(&softc->trim_queue, bp); | |||||
TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); | TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); | ||||
cam_fill_ataio(ataio, | cam_fill_ataio(ataio, | ||||
ada_retry_count, | ada_retry_count, | ||||
adadone, | adadone, | ||||
CAM_DIR_NONE, | CAM_DIR_NONE, | ||||
0, | 0, | ||||
NULL, | NULL, | ||||
Show All 14 Lines | adastart(struct cam_periph *periph, union ccb *start_ccb) | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n")); | ||||
switch (softc->state) { | switch (softc->state) { | ||||
case ADA_STATE_NORMAL: | case ADA_STATE_NORMAL: | ||||
{ | { | ||||
struct bio *bp; | struct bio *bp; | ||||
u_int8_t tag_code; | u_int8_t tag_code; | ||||
/* Run TRIM if not running yet. */ | bp = cam_iosched_next_bio(softc->cam_iosched); | ||||
if (!softc->trim_running && | |||||
(bp = bioq_first(&softc->trim_queue)) != 0) { | |||||
if (softc->flags & ADA_FLAG_CAN_TRIM) { | |||||
ada_dsmtrim(softc, bp, ataio); | |||||
} else if ((softc->flags & ADA_FLAG_CAN_CFA) && | |||||
!(softc->flags & ADA_FLAG_CAN_48BIT)) { | |||||
ada_cfaerase(softc, bp, ataio); | |||||
} else { | |||||
/* This can happen if DMA was disabled. */ | |||||
bioq_remove(&softc->trim_queue, bp); | |||||
biofinish(bp, NULL, EOPNOTSUPP); | |||||
xpt_release_ccb(start_ccb); | |||||
adaschedule(periph); | |||||
return; | |||||
} | |||||
softc->trim_running = 1; | |||||
start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; | |||||
start_ccb->ccb_h.flags |= CAM_UNLOCKED; | |||||
goto out; | |||||
} | |||||
/* Run regular command. */ | |||||
bp = bioq_first(&softc->bio_queue); | |||||
if (bp == NULL) { | if (bp == NULL) { | ||||
xpt_release_ccb(start_ccb); | xpt_release_ccb(start_ccb); | ||||
break; | break; | ||||
} | } | ||||
bioq_remove(&softc->bio_queue, bp); | |||||
if ((bp->bio_flags & BIO_ORDERED) != 0 | if ((bp->bio_flags & BIO_ORDERED) != 0 || | ||||
|| (softc->flags & ADA_FLAG_NEED_OTAG) != 0) { | (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) { | ||||
softc->flags &= ~ADA_FLAG_NEED_OTAG; | softc->flags &= ~ADA_FLAG_NEED_OTAG; | ||||
softc->flags |= ADA_FLAG_WAS_OTAG; | softc->flags |= ADA_FLAG_WAS_OTAG; | ||||
tag_code = 0; | tag_code = 0; | ||||
} else { | } else { | ||||
tag_code = 1; | tag_code = 1; | ||||
} | } | ||||
switch (bp->bio_cmd) { | switch (bp->bio_cmd) { | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | #endif | ||||
} else { | } else { | ||||
ata_28bit_cmd(ataio, ATA_WRITE_MUL, | ata_28bit_cmd(ataio, ATA_WRITE_MUL, | ||||
0, lba, count); | 0, lba, count); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case BIO_DELETE: | |||||
switch (softc->delete_method) { | |||||
case ADA_DELETE_NCQ_DSM_TRIM: | |||||
ada_ncq_dsmtrim(softc, bp, ataio); | |||||
break; | |||||
case ADA_DELETE_DSM_TRIM: | |||||
ada_dsmtrim(softc, bp, ataio); | |||||
break; | |||||
case ADA_DELETE_CFA_ERASE: | |||||
ada_cfaerase(softc, bp, ataio); | |||||
break; | |||||
default: | |||||
biofinish(bp, NULL, EOPNOTSUPP); | |||||
xpt_release_ccb(start_ccb); | |||||
adaschedule(periph); | |||||
return; | |||||
} | |||||
start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; | |||||
start_ccb->ccb_h.flags |= CAM_UNLOCKED; | |||||
cam_iosched_submit_trim(softc->cam_iosched); | |||||
goto out; | |||||
case BIO_FLUSH: | case BIO_FLUSH: | ||||
cam_fill_ataio(ataio, | cam_fill_ataio(ataio, | ||||
1, | 1, | ||||
adadone, | adadone, | ||||
CAM_DIR_NONE, | CAM_DIR_NONE, | ||||
0, | 0, | ||||
NULL, | NULL, | ||||
0, | 0, | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | adadone(struct cam_periph *periph, union ccb *done_ccb) | ||||
switch (state) { | switch (state) { | ||||
case ADA_CCB_BUFFER_IO: | case ADA_CCB_BUFFER_IO: | ||||
case ADA_CCB_TRIM: | case ADA_CCB_TRIM: | ||||
{ | { | ||||
struct bio *bp; | struct bio *bp; | ||||
int error; | int error; | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
bp = (struct bio *)done_ccb->ccb_h.ccb_bp; | |||||
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | ||||
error = adaerror(done_ccb, 0, 0); | error = adaerror(done_ccb, 0, 0); | ||||
if (error == ERESTART) { | if (error == ERESTART) { | ||||
/* A retry was scheduled, so just return. */ | /* A retry was scheduled, so just return. */ | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
return; | return; | ||||
} | } | ||||
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | ||||
cam_release_devq(path, | cam_release_devq(path, | ||||
/*relsim_flags*/0, | /*relsim_flags*/0, | ||||
/*reduction*/0, | /*reduction*/0, | ||||
/*timeout*/0, | /*timeout*/0, | ||||
/*getcount_only*/0); | /*getcount_only*/0); | ||||
/* | |||||
* If we get an error on an NCQ DSM TRIM, fall back | |||||
* to a non-NCQ DSM TRIM forever. Please note that if | |||||
* CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too. | |||||
* However, for this one trim, we treat it as advisory | |||||
* and return success up the stack. | |||||
*/ | |||||
if (state == ADA_CCB_TRIM && | |||||
error != 0 && | |||||
(softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) { | |||||
softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; | |||||
error = 0; | |||||
adasetdeletemethod(softc); | |||||
} | |||||
} else { | } else { | ||||
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | ||||
panic("REQ_CMP with QFRZN"); | panic("REQ_CMP with QFRZN"); | ||||
error = 0; | error = 0; | ||||
} | } | ||||
bp = (struct bio *)done_ccb->ccb_h.ccb_bp; | |||||
bp->bio_error = error; | bp->bio_error = error; | ||||
if (error != 0) { | if (error != 0) { | ||||
bp->bio_resid = bp->bio_bcount; | bp->bio_resid = bp->bio_bcount; | ||||
bp->bio_flags |= BIO_ERROR; | bp->bio_flags |= BIO_ERROR; | ||||
} else { | } else { | ||||
if (state == ADA_CCB_TRIM) | if (state == ADA_CCB_TRIM) | ||||
bp->bio_resid = 0; | bp->bio_resid = 0; | ||||
else | else | ||||
bp->bio_resid = ataio->resid; | bp->bio_resid = ataio->resid; | ||||
if (bp->bio_resid > 0) | if (bp->bio_resid > 0) | ||||
bp->bio_flags |= BIO_ERROR; | bp->bio_flags |= BIO_ERROR; | ||||
} | } | ||||
softc->outstanding_cmds--; | softc->outstanding_cmds--; | ||||
if (softc->outstanding_cmds == 0) | if (softc->outstanding_cmds == 0) | ||||
softc->flags |= ADA_FLAG_WAS_OTAG; | softc->flags |= ADA_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 == ADA_CCB_TRIM) { | if (state == ADA_CCB_TRIM) { | ||||
TAILQ_HEAD(, bio) queue; | TAILQ_HEAD(, bio) queue; | ||||
struct bio *bp1; | struct bio *bp1; | ||||
TAILQ_INIT(&queue); | TAILQ_INIT(&queue); | ||||
TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue); | TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue); | ||||
/* | /* | ||||
* 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 | ||||
* trim_running set to 0 before the call above to allow | * trim_running set to 0 before the call above to allow | ||||
* other I/O to progress when many BIO_DELETE requests | * other I/O to progress when many BIO_DELETE requests | ||||
* are pushed down. We set trim_running to 0 and call | * are pushed down. We set trim_running to 0 and call | ||||
* daschedule again so that we don't stall if there are | * daschedule again so that we don't stall if there are | ||||
* no other I/Os pending apart from BIO_DELETEs. | * no other I/Os pending apart from BIO_DELETEs. | ||||
*/ | */ | ||||
softc->trim_running = 0; | cam_iosched_trim_done(softc->cam_iosched); | ||||
adaschedule(periph); | adaschedule(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 = error; | bp1->bio_error = error; | ||||
if (error != 0) { | if (error != 0) { | ||||
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 { | ||||
adaschedule(periph); | |||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
biodone(bp); | biodone(bp); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
case ADA_CCB_RAHEAD: | case ADA_CCB_RAHEAD: | ||||
{ | { | ||||
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | default: | ||||
break; | break; | ||||
} | } | ||||
xpt_release_ccb(done_ccb); | xpt_release_ccb(done_ccb); | ||||
} | } | ||||
static int | static int | ||||
adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) | adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) | ||||
{ | { | ||||
struct ada_softc *softc; | |||||
struct cam_periph *periph; | |||||
periph = xpt_path_periph(ccb->ccb_h.path); | |||||
softc = (struct ada_softc *)periph->softc; | |||||
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: | |||||
case CAM_ATA_STATUS_ERROR: | |||||
#ifdef CAM_IO_STATS | |||||
softc->errors++; | |||||
#endif | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return(cam_periph_error(ccb, cam_flags, sense_flags, NULL)); | return(cam_periph_error(ccb, cam_flags, sense_flags, NULL)); | ||||
} | } | ||||
static void | static void | ||||
adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd) | adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd) | ||||
{ | { | ||||
struct ada_softc *softc = (struct ada_softc *)periph->softc; | struct ada_softc *softc = (struct ada_softc *)periph->softc; | ||||
▲ Show 20 Lines • Show All 213 Lines • Show Last 20 Lines |