summaryrefslogtreecommitdiffstats
path: root/sys/cam/cam_xpt.c
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2001-03-27 05:45:52 +0000
committerken <ken@FreeBSD.org>2001-03-27 05:45:52 +0000
commit24c4b1e75b8af36f530a3eabf2d1a17810637a5f (patch)
tree922f780aaaef7c90e6112b8537c9a1b7abb5b0c7 /sys/cam/cam_xpt.c
parent37504f69c9fd5f7dee2b8de312ee783f169777a9 (diff)
downloadFreeBSD-src-24c4b1e75b8af36f530a3eabf2d1a17810637a5f.zip
FreeBSD-src-24c4b1e75b8af36f530a3eabf2d1a17810637a5f.tar.gz
Rewrite of the CAM error recovery code.
Some of the major changes include: - The SCSI error handling portion of cam_periph_error() has been broken out into a number of subfunctions to better modularize the code that handles the hierarchy of SCSI errors. As a result, the code is now much easier to read. - String handling and error printing has been significantly revamped. We now use sbufs to do string formatting instead of using printfs (for the kernel) and snprintf/strncat (for userland) as before. There is a new catchall error printing routine, cam_error_print() and its string-based counterpart, cam_error_string() that allow the kernel and userland applications to pass in a CCB and have errors printed out properly, whether or not they're SCSI errors. Among other things, this helped eliminate a fair amount of duplicate code in camcontrol. We now print out more information than before, including the CAM status and SCSI status and the error recovery action taken to remedy the problem. - sbufs are now available in userland, via libsbuf. This change was necessary since most of the error printing code is shared between libcam and the kernel. - A new transfer settings interface is included in this checkin. This code is #ifdef'ed out, and is primarily intended to aid discussion with HBA driver authors on the final form the interface should take. There is example code in the ahc(4) driver that implements the HBA driver side of the new interface. The new transfer settings code won't be enabled until we're ready to switch all HBA drivers over to the new interface. src/Makefile.inc1, lib/Makefile: Add libsbuf. It must be built before libcam, since libcam uses sbuf routines. libcam/Makefile: libcam now depends on libsbuf. libsbuf/Makefile: Add a makefile for libsbuf. This pulls in the sbuf sources from sys/kern. bsd.libnames.mk: Add LIBSBUF. camcontrol/Makefile: Add -lsbuf. Since camcontrol is statically linked, we can't depend on the dynamic linker to pull in libsbuf. camcontrol.c: Use cam_error_print() instead of checking for CAM_SCSI_STATUS_ERROR on every failed CCB. sbuf.9: Change the prototypes for sbuf_cat() and sbuf_cpy() so that the source string is now a const char *. This is more in line wth the standard system string functions, and helps eliminate warnings when dealing with a const source buffer. Fix a typo. cam.c: Add description strings for the various CAM error status values, as well as routines to look up those strings. Add new cam_error_string() and cam_error_print() routines for userland and the kernel. cam.h: Add a new CAM flag, CAM_RETRY_SELTO. Add enumerated types for the various options available with cam_error_print() and cam_error_string(). cam_ccb.h: Add new transfer negotiation structures/types. Change inq_len in the ccb_getdev structure to be "reserved". This field has never been filled in, and will be removed when we next bump the CAM version. cam_debug.h: Fix typo. cam_periph.c: Modularize cam_periph_error(). The SCSI error handling part of cam_periph_error() is now in camperiphscsistatuserror() and camperiphscsisenseerror(). In cam_periph_lock(), increase the reference count on the periph while we wait for our lock attempt to succeed so that the periph won't go away while we're sleeping. cam_xpt.c: Add new transfer negotiation code. (ifdefed out) Add a new function, xpt_path_string(). This is a string/sbuf analog to xpt_print_path(). scsi_all.c: Revamp string handing and error printing code. We now use sbufs for much of the string formatting code. More of that code is shared between userland the kernel. scsi_all.h: Get rid of SS_TURSTART, it wasn't terribly useful in the first place. Add a new error action, SS_REQSENSE. (Send a request sense and then retry the command.) This is useful when the controller hasn't performed autosense for some reason. Change the default actions around a bit. scsi_cd.c, scsi_da.c, scsi_pt.c, scsi_ses.c: SF_RETRY_SELTO -> CAM_RETRY_SELTO. Selection timeouts shouldn't be covered by a sense flag. scsi_pass.[ch]: SF_RETRY_SELTO -> CAM_RETRY_SELTO. Get rid of the last vestiges of a read/write interface. libkern/bsearch.c, sys/libkern.h, conf/files: Add bsearch.c, which is needed for some of the new table lookup routines. aic7xxx_freebsd.c: Define AHC_NEW_TRAN_SETTINGS if CAM_NEW_TRAN_CODE is defined. sbuf.h, subr_sbuf.c: Add the appropriate #ifdefs so sbufs can compile and run in userland. Change sbuf_printf() to use vsnprintf() instead of kvprintf(), which is only available in the kernel. Change the source string for sbuf_cpy() and sbuf_cat() to be a const char *. Add __BEGIN_DECLS and __END_DECLS around function prototypes since they're now exported to userland. kdump/mkioctls: Include stdio.h before cam.h since cam.h now includes a function with a FILE * argument. Submitted by: gibbs (mostly) Reviewed by: jdp, marcel (libsbuf makefile changes) Reviewed by: des (sbuf changes) Reviewed by: ken
Diffstat (limited to 'sys/cam/cam_xpt.c')
-rw-r--r--sys/cam/cam_xpt.c636
1 files changed, 608 insertions, 28 deletions
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index 5163d20..d1ddf2a 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -40,6 +40,7 @@
#include <sys/md5.h>
#include <sys/devicestat.h>
#include <sys/interrupt.h>
+#include <sys/sbuf.h>
#ifdef PC98
#include <pc98/pc98/pc98_machdep.h> /* geometry translation */
@@ -122,7 +123,13 @@ struct cam_ed {
struct cam_periph *owner; /* Peripheral driver's ownership tag */
struct xpt_quirk_entry *quirk; /* Oddities about this device */
/* Storage for the inquiry data */
- struct scsi_inquiry_data inq_data;
+#ifdef CAM_NEW_TRAN_CODE
+ cam_proto protocol;
+ u_int protocol_version;
+ cam_xport transport;
+ u_int transport_version;
+#endif /* CAM_NEW_TRAN_CODE */
+ struct scsi_inquiry_data inq_data;
u_int8_t inq_flags; /*
* Current settings for inquiry flags.
* This allows us to override settings
@@ -131,7 +138,7 @@ struct cam_ed {
*/
u_int8_t queue_flags; /* Queue flags from the control page */
u_int8_t serial_num_len;
- u_int8_t *serial_num;
+ u_int8_t *serial_num;
u_int32_t qfrozen_cnt;
u_int32_t flags;
#define CAM_DEV_UNCONFIGURED 0x01
@@ -718,11 +725,12 @@ static void xptasync(struct cam_periph *periph,
u_int32_t code, cam_path *path);
#endif
static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns,
- int num_patterns, struct cam_eb *bus);
+ u_int num_patterns, struct cam_eb *bus);
static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns,
- int num_patterns, struct cam_ed *device);
+ u_int num_patterns,
+ struct cam_ed *device);
static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns,
- int num_patterns,
+ u_int num_patterns,
struct cam_periph *periph);
static xpt_busfunc_t xptedtbusfunc;
static xpt_targetfunc_t xptedttargetfunc;
@@ -776,6 +784,9 @@ static void proberequestdefaultnegotiation(struct cam_periph *periph);
static void probedone(struct cam_periph *periph, union ccb *done_ccb);
static void probecleanup(struct cam_periph *periph);
static void xpt_find_quirk(struct cam_ed *device);
+#ifdef CAM_NEW_TRAN_CODE
+static void xpt_devise_transport(struct cam_path *path);
+#endif /* CAM_NEW_TRAN_CODE */
static void xpt_set_transfer_settings(struct ccb_trans_settings *cts,
struct cam_ed *device,
int async_update);
@@ -1129,8 +1140,8 @@ xptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
struct cam_periph *periph;
struct periph_driver **p_drv;
char *name;
- int unit;
- int cur_generation;
+ u_int unit;
+ u_int cur_generation;
int base_periph_found;
int splbreaknum;
int s;
@@ -1464,6 +1475,118 @@ xpt_remove_periph(struct cam_periph *periph)
}
+#ifdef CAM_NEW_TRAN_CODE
+
+void
+xpt_announce_periph(struct cam_periph *periph, char *announce_string)
+{
+ struct ccb_pathinq cpi;
+ struct ccb_trans_settings cts;
+ struct cam_path *path;
+ u_int speed;
+ u_int freq;
+ u_int mb;
+ int s;
+
+ path = periph->path;
+ /*
+ * To ensure that this is printed in one piece,
+ * mask out CAM interrupts.
+ */
+ s = splsoftcam();
+ printf("%s%d at %s%d bus %d target %d lun %d\n",
+ periph->periph_name, periph->unit_number,
+ path->bus->sim->sim_name,
+ path->bus->sim->unit_number,
+ path->bus->sim->bus_id,
+ path->target->target_id,
+ path->device->lun_id);
+ printf("%s%d: ", periph->periph_name, periph->unit_number);
+ scsi_print_inquiry(&path->device->inq_data);
+ if ((bootverbose)
+ && (path->device->serial_num_len > 0)) {
+ /* Don't wrap the screen - print only the first 60 chars */
+ printf("%s%d: Serial Number %.60s\n", periph->periph_name,
+ periph->unit_number, path->device->serial_num);
+ }
+ xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1);
+ cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+ xpt_action((union ccb*)&cts);
+
+ /* Ask the SIM for its base transfer speed */
+ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ speed = cpi.base_transfer_speed;
+ freq = 0;
+ if (cts.ccb_h.status == CAM_REQ_CMP
+ && cts.transport == XPORT_SPI) {
+ struct ccb_trans_settings_spi *spi;
+
+ spi = &cts.xport_specific.spi;
+ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0
+ && spi->sync_offset != 0) {
+ freq = scsi_calc_syncsrate(spi->sync_period);
+ speed = freq;
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0)
+ speed *= (0x01 << spi->bus_width);
+ }
+
+ mb = speed / 1000;
+ if (mb > 0)
+ printf("%s%d: %d.%03dMB/s transfers",
+ periph->periph_name, periph->unit_number,
+ mb, speed % 1000);
+ else
+ printf("%s%d: %dKB/s transfers", periph->periph_name,
+ periph->unit_number, speed);
+ /* Report additional information about SPI connections */
+ if (cts.ccb_h.status == CAM_REQ_CMP
+ && cts.transport == XPORT_SPI) {
+ struct ccb_trans_settings_spi *spi;
+
+ spi = &cts.xport_specific.spi;
+ if (freq != 0) {
+ printf(" (%d.%03dMHz%s, offset %d", freq / 1000,
+ freq % 1000,
+ (spi->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ ? " DT" : "",
+ spi->sync_offset);
+ }
+ if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0
+ && spi->bus_width > 0) {
+ if (freq != 0) {
+ printf(", ");
+ } else {
+ printf(" (");
+ }
+ printf("%dbit)", 8 * (0x01 << spi->bus_width));
+ } else if (freq != 0) {
+ printf(")");
+ }
+ }
+
+ if (path->device->inq_flags & SID_CmdQue
+ || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) {
+ printf("\n%s%d: Tagged Queueing Enabled",
+ periph->periph_name, periph->unit_number);
+ }
+ printf("\n");
+
+ /*
+ * We only want to print the caller's announce string if they've
+ * passed one in..
+ */
+ if (announce_string != NULL)
+ printf("%s%d: %s\n", periph->periph_name,
+ periph->unit_number, announce_string);
+ splx(s);
+}
+#else /* CAM_NEW_TRAN_CODE */
void
xpt_announce_periph(struct cam_periph *periph, char *announce_string)
{
@@ -1567,9 +1690,10 @@ xpt_announce_periph(struct cam_periph *periph, char *announce_string)
splx(s);
}
+#endif /* CAM_NEW_TRAN_CODE */
static dev_match_ret
-xptbusmatch(struct dev_match_pattern *patterns, int num_patterns,
+xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns,
struct cam_eb *bus)
{
dev_match_ret retval;
@@ -1681,7 +1805,7 @@ xptbusmatch(struct dev_match_pattern *patterns, int num_patterns,
}
static dev_match_ret
-xptdevicematch(struct dev_match_pattern *patterns, int num_patterns,
+xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns,
struct cam_ed *device)
{
dev_match_ret retval;
@@ -1797,7 +1921,7 @@ xptdevicematch(struct dev_match_pattern *patterns, int num_patterns,
* Match a single peripheral against any number of match patterns.
*/
static dev_match_ret
-xptperiphmatch(struct dev_match_pattern *patterns, int num_patterns,
+xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns,
struct cam_periph *periph)
{
dev_match_ret retval;
@@ -2778,6 +2902,9 @@ xpt_action(union ccb *start_ccb)
switch (start_ccb->ccb_h.func_code) {
case XPT_SCSI_IO:
{
+#ifdef CAM_NEW_TRAN_CODE
+ struct cam_ed *device;
+#endif /* CAM_NEW_TRAN_CODE */
#ifdef CAMDEBUG
char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
struct cam_path *path;
@@ -2801,7 +2928,12 @@ xpt_action(union ccb *start_ccb)
* This means that this code will be exercised while probing
* devices with an ANSI revision greater than 2.
*/
+#ifdef CAM_NEW_TRAN_CODE
+ device = start_ccb->ccb_h.path->device;
+ if (device->protocol_version <= SCSI_REV_2
+#else /* CAM_NEW_TRAN_CODE */
if (SID_ANSI_REV(&start_ccb->ccb_h.path->device->inq_data) <= 2
+#endif /* CAM_NEW_TRAN_CODE */
&& start_ccb->ccb_h.target_lun < 8
&& (start_ccb->ccb_h.flags & CAM_CDB_POINTER) == 0) {
@@ -3023,7 +3155,7 @@ xpt_action(union ccb *start_ccb)
struct cam_periph *nperiph;
struct periph_list *periph_head;
struct ccb_getdevlist *cgdl;
- int i;
+ u_int i;
int s;
struct cam_ed *device;
int found;
@@ -3115,7 +3247,7 @@ xpt_action(union ccb *start_ccb)
if (cdm->pos.position_type != CAM_DEV_POS_NONE)
position_type = cdm->pos.position_type;
else {
- int i;
+ u_int i;
position_type = CAM_DEV_POS_NONE;
@@ -3980,6 +4112,44 @@ xpt_print_path(struct cam_path *path)
}
}
+int
+xpt_path_string(struct cam_path *path, char *str, size_t str_len)
+{
+ struct sbuf sb;
+
+ sbuf_new(&sb, str, str_len, 0);
+
+ if (path == NULL)
+ sbuf_printf(&sb, "(nopath): ");
+ else {
+ if (path->periph != NULL)
+ sbuf_printf(&sb, "(%s%d:", path->periph->periph_name,
+ path->periph->unit_number);
+ else
+ sbuf_printf(&sb, "(noperiph:");
+
+ if (path->bus != NULL)
+ sbuf_printf(&sb, "%s%d:%d:", path->bus->sim->sim_name,
+ path->bus->sim->unit_number,
+ path->bus->sim->bus_id);
+ else
+ sbuf_printf(&sb, "nobus:");
+
+ if (path->target != NULL)
+ sbuf_printf(&sb, "%d:", path->target->target_id);
+ else
+ sbuf_printf(&sb, "X:");
+
+ if (path->device != NULL)
+ sbuf_printf(&sb, "%d): ", path->device->lun_id);
+ else
+ sbuf_printf(&sb, "X): ");
+ }
+ sbuf_finish(&sb);
+
+ return(sbuf_len(&sb));
+}
+
path_id_t
xpt_path_path_id(struct cam_path *path)
{
@@ -4115,7 +4285,7 @@ xpt_bus_register(struct cam_sim *sim, u_int32_t bus)
xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
- xpt_async(AC_PATH_REGISTERED, xpt_periph->path, &cpi);
+ xpt_async(AC_PATH_REGISTERED, &path, &cpi);
xpt_release_path(&path);
}
return (CAM_SUCCESS);
@@ -4693,6 +4863,9 @@ xpt_release_target(struct cam_eb *bus, struct cam_et *target)
static struct cam_ed *
xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
{
+#ifdef CAM_NEW_TRAN_CODE
+ struct cam_path path;
+#endif /* CAM_NEW_TRAN_CODE */
struct cam_ed *device;
struct cam_devq *devq;
cam_status status;
@@ -4769,6 +4942,17 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
TAILQ_INSERT_TAIL(&target->ed_entries, device, links);
}
target->generation++;
+#ifdef CAM_NEW_TRAN_CODE
+ if (lun_id != CAM_LUN_WILDCARD) {
+ xpt_compile_path(&path,
+ NULL,
+ bus->path_id,
+ target->target_id,
+ lun_id);
+ xpt_devise_transport(&path);
+ xpt_release_path(&path);
+ }
+#endif /* CAM_NEW_TRAN_CODE */
}
return (device);
}
@@ -4964,10 +5148,6 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
work_ccb->ccb_h.cbfcnp = xpt_scan_bus;
work_ccb->ccb_h.ppriv_ptr0 = scan_info;
work_ccb->crcn.flags = request_ccb->crcn.flags;
-#if 0
- printf("xpt_scan_bus: probing %d:%d:%d\n",
- request_ccb->ccb_h.path_id, i, 0);
-#endif
xpt_action(work_ccb);
}
break;
@@ -4990,11 +5170,6 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
lun_id = request_ccb->ccb_h.target_lun;
xpt_action(request_ccb);
-#if 0
- printf("xpt_scan_bus: got back probe from %d:%d:%d\n",
- path_id, target_id, lun_id);
-#endif
-
if (request_ccb->ccb_h.status != CAM_REQ_CMP) {
struct cam_ed *device;
struct cam_et *target;
@@ -5087,10 +5262,6 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
request_ccb->ccb_h.ppriv_ptr0 = scan_info;
request_ccb->crcn.flags =
scan_info->request_ccb->crcn.flags;
-#if 0
- xpt_print_path(path);
- printf("xpt_scan bus probing\n");
-#endif
xpt_action(request_ccb);
}
break;
@@ -5468,11 +5639,19 @@ proberequestdefaultnegotiation(struct cam_periph *periph)
xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+#ifdef CAM_NEW_TRAN_CODE
+ cts.type = CTS_TYPE_USER_SETTINGS;
+#else /* CAM_NEW_TRAN_CODE */
cts.flags = CCB_TRANS_USER_SETTINGS;
+#endif /* CAM_NEW_TRAN_CODE */
xpt_action((union ccb *)&cts);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+#ifdef CAM_NEW_TRAN_CODE
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+#else /* CAM_NEW_TRAN_CODE */
cts.flags &= ~CCB_TRANS_USER_SETTINGS;
cts.flags |= CCB_TRANS_CURRENT_SETTINGS;
+#endif /* CAM_NEW_TRAN_CODE */
xpt_action((union ccb *)&cts);
}
@@ -5550,6 +5729,9 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
xpt_find_quirk(path->device);
+#ifdef CAM_NEW_TRAN_CODE
+ xpt_devise_transport(path);
+#endif /* CAM_NEW_TRAN_CODE */
if ((inq_buf->flags & SID_CmdQue) != 0)
softc->action =
PROBE_MODE_SENSE;
@@ -5788,6 +5970,379 @@ xpt_find_quirk(struct cam_ed *device)
device->quirk = (struct xpt_quirk_entry *)match;
}
+#ifdef CAM_NEW_TRAN_CODE
+
+static void
+xpt_devise_transport(struct cam_path *path)
+{
+ struct ccb_pathinq cpi;
+ struct ccb_trans_settings cts;
+ struct scsi_inquiry_data *inq_buf;
+
+ /* Get transport information from the SIM */
+ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ inq_buf = NULL;
+ if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0)
+ inq_buf = &path->device->inq_data;
+ path->device->protocol = PROTO_SCSI;
+ path->device->protocol_version =
+ inq_buf != NULL ? SID_ANSI_REV(inq_buf) : cpi.protocol_version;
+ path->device->transport = cpi.transport;
+ path->device->transport_version = cpi.transport_version;
+
+ /*
+ * Any device not using SPI3 features should
+ * be considered SPI2 or lower.
+ */
+ if (inq_buf != NULL) {
+ if (path->device->transport == XPORT_SPI
+ && (inq_buf->spi3data & SID_SPI_MASK) == 0
+ && path->device->transport_version > 2)
+ path->device->transport_version = 2;
+ } else {
+ struct cam_ed* otherdev;
+
+ for (otherdev = TAILQ_FIRST(&path->target->ed_entries);
+ otherdev != NULL;
+ otherdev = TAILQ_NEXT(otherdev, links)) {
+ if (otherdev != path->device)
+ break;
+ }
+
+ if (otherdev != NULL) {
+ /*
+ * Initially assume the same versioning as
+ * prior luns for this target.
+ */
+ path->device->protocol_version =
+ otherdev->protocol_version;
+ path->device->transport_version =
+ otherdev->transport_version;
+ } else {
+ /* Until we know better, opt for safty */
+ path->device->protocol_version = 2;
+ if (path->device->transport == XPORT_SPI)
+ path->device->transport_version = 2;
+ else
+ path->device->transport_version = 0;
+ }
+ }
+
+ /*
+ * XXX
+ * For a device compliant with SPC-2 we should be able
+ * to determine the transport version supported by
+ * scrutinizing the version descriptors in the
+ * inquiry buffer.
+ */
+
+ /* Tell the controller what we think */
+ xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1);
+ cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+ cts.transport = path->device->transport;
+ cts.transport_version = path->device->transport_version;
+ cts.protocol = path->device->protocol;
+ cts.protocol_version = path->device->protocol_version;
+ cts.proto_specific.valid = 0;
+ cts.xport_specific.valid = 0;
+ xpt_action((union ccb *)&cts);
+}
+
+static void
+xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
+ int async_update)
+{
+ struct ccb_pathinq cpi;
+ struct ccb_trans_settings cur_cts;
+ struct ccb_trans_settings_scsi *scsi;
+ struct ccb_trans_settings_scsi *cur_scsi;
+ struct cam_sim *sim;
+ struct scsi_inquiry_data *inq_data;
+
+ if (device == NULL) {
+ cts->ccb_h.status = CAM_PATH_INVALID;
+ xpt_done((union ccb *)cts);
+ return;
+ }
+
+ if (cts->protocol == PROTO_UNKNOWN
+ || cts->protocol == PROTO_UNSPECIFIED) {
+ cts->protocol = device->protocol;
+ cts->protocol_version = device->protocol_version;
+ }
+
+ if (cts->protocol_version == PROTO_VERSION_UNKNOWN
+ || cts->protocol_version == PROTO_VERSION_UNSPECIFIED)
+ cts->protocol_version = device->protocol_version;
+
+ if (cts->protocol != device->protocol) {
+ xpt_print_path(cts->ccb_h.path);
+ printf("Uninitialized Protocol %x:%x?\n",
+ cts->protocol, device->protocol);
+ cts->protocol = device->protocol;
+ }
+
+ if (cts->protocol_version > device->protocol_version) {
+ if (bootverbose) {
+ xpt_print_path(cts->ccb_h.path);
+ printf("Down reving Protocol Version from %d to %d?\n",
+ cts->protocol_version, device->protocol_version);
+ }
+ cts->protocol_version = device->protocol_version;
+ }
+
+ if (cts->transport == XPORT_UNKNOWN
+ || cts->transport == XPORT_UNSPECIFIED) {
+ cts->transport = device->transport;
+ cts->transport_version = device->transport_version;
+ }
+
+ if (cts->transport_version == XPORT_VERSION_UNKNOWN
+ || cts->transport_version == XPORT_VERSION_UNSPECIFIED)
+ cts->transport_version = device->transport_version;
+
+ if (cts->transport != device->transport) {
+ xpt_print_path(cts->ccb_h.path);
+ printf("Uninitialized Transport %x:%x?\n",
+ cts->transport, device->transport);
+ cts->transport = device->transport;
+ }
+
+ if (cts->transport_version > device->transport_version) {
+ if (bootverbose) {
+ xpt_print_path(cts->ccb_h.path);
+ printf("Down reving Transport Version from %d to %d?\n",
+ cts->transport_version,
+ device->transport_version);
+ }
+ cts->transport_version = device->transport_version;
+ }
+
+ sim = cts->ccb_h.path->bus->sim;
+
+ /*
+ * Nothing more of interest to do unless
+ * this is a device connected via the
+ * SCSI protocol.
+ */
+ if (cts->protocol != PROTO_SCSI) {
+ if (async_update == FALSE)
+ (*(sim->sim_action))(sim, (union ccb *)cts);
+ return;
+ }
+
+ inq_data = &device->inq_data;
+ scsi = &cts->proto_specific.scsi;
+ xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, /*priority*/1);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ /* SCSI specific sanity checking */
+ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0
+ || (inq_data->flags & SID_CmdQue) == 0
+ || (device->queue_flags & SCP_QUEUE_DQUE) != 0
+ || (device->quirk->mintags == 0)) {
+ /*
+ * Can't tag on hardware that doesn't support tags,
+ * doesn't have it enabled, or has broken tag support.
+ */
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ }
+
+ if (async_update == FALSE) {
+ /*
+ * Perform sanity checking against what the
+ * controller and device can do.
+ */
+ xpt_setup_ccb(&cur_cts.ccb_h, cts->ccb_h.path, /*priority*/1);
+ cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ cur_cts.type = cts->type;
+ xpt_action((union ccb *)&cur_cts);
+
+ cur_scsi = &cur_cts.proto_specific.scsi;
+ if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) {
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ scsi->flags |= cur_scsi->flags & CTS_SCSI_FLAGS_TAG_ENB;
+ }
+ if ((cur_scsi->valid & CTS_SCSI_VALID_TQ) == 0)
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ }
+
+ /* SPI specific sanity checking */
+ if (cts->transport == XPORT_SPI
+ && async_update == FALSE) {
+ u_int spi3caps;
+ struct ccb_trans_settings_spi *spi;
+ struct ccb_trans_settings_spi *cur_spi;
+
+ spi = &cts->xport_specific.spi;
+
+ cur_spi = &cur_cts.xport_specific.spi;
+
+ /* Fill in any gaps in what the user gave us */
+ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0)
+ spi->sync_period = cur_spi->sync_period;
+ if ((cur_spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0)
+ spi->sync_period = 0;
+ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0)
+ spi->sync_offset = cur_spi->sync_offset;
+ if ((cur_spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0)
+ spi->sync_offset = 0;
+ if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0)
+ spi->ppr_options = cur_spi->ppr_options;
+ if ((cur_spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0)
+ spi->ppr_options = 0;
+ if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0)
+ spi->bus_width = cur_spi->bus_width;
+ if ((cur_spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0)
+ spi->bus_width = 0;
+ if ((spi->valid & CTS_SPI_VALID_DISC) == 0) {
+ spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB;
+ spi->flags |= cur_spi->flags & CTS_SPI_FLAGS_DISC_ENB;
+ }
+ if ((cur_spi->valid & CTS_SPI_VALID_DISC) == 0)
+ spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB;
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0
+ && (inq_data->flags & SID_Sync) == 0
+ && cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ || ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0)
+ || (cts->sync_offset == 0)
+ || (cts->sync_period == 0)) {
+ /* Force async */
+ spi->sync_period = 0;
+ spi->sync_offset = 0;
+ }
+
+ switch (spi->bus_width) {
+ case MSG_EXT_WDTR_BUS_32_BIT:
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0
+ || (inq_data->flags & SID_WBus32) != 0
+ || cts->type == CTS_TYPE_USER_SETTINGS)
+ && (cpi.hba_inquiry & PI_WIDE_32) != 0)
+ break;
+ /* Fall Through to 16-bit */
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0
+ || (inq_data->flags & SID_WBus16) != 0
+ || cts->type == CTS_TYPE_USER_SETTINGS)
+ && (cpi.hba_inquiry & PI_WIDE_16) != 0) {
+ spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* Fall Through to 8-bit */
+ default: /* New bus width?? */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ /* All targets can do this */
+ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+
+ spi3caps = cpi.xport_specific.spi.ppr_options;
+ if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0
+ && cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ spi3caps &= inq_data->spi3data;
+
+ if ((spi3caps & SID_SPI_CLOCK_DT) == 0)
+ spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ if ((spi3caps & SID_SPI_IUS) == 0)
+ spi->ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+
+ if ((spi3caps & SID_SPI_QAS) == 0)
+ spi->ppr_options &= ~MSG_EXT_PPR_QAS_REQ;
+
+ /* No SPI Transfer settings are allowed unless we are wide */
+ if (spi->bus_width == 0)
+ spi->ppr_options = 0;
+
+ if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) == 0) {
+ /*
+ * Can't tag queue without disconnection.
+ */
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ scsi->valid |= CTS_SCSI_VALID_TQ;
+ }
+
+ /*
+ * If we are currently performing tagged transactions to
+ * this device and want to change its negotiation parameters,
+ * go non-tagged for a bit to give the controller a chance to
+ * negotiate unhampered by tag messages.
+ */
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS
+ && (device->inq_flags & SID_CmdQue) != 0
+ && (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0
+ && (spi->flags & (CTS_SPI_VALID_SYNC_RATE|
+ CTS_SPI_VALID_SYNC_OFFSET|
+ CTS_SPI_VALID_BUS_WIDTH)) != 0)
+ xpt_toggle_tags(cts->ccb_h.path);
+ }
+
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS
+ && (scsi->valid & CTS_SCSI_VALID_TQ) != 0) {
+ int device_tagenb;
+
+ /*
+ * If we are transitioning from tags to no-tags or
+ * vice-versa, we need to carefully freeze and restart
+ * the queue so that we don't overlap tagged and non-tagged
+ * commands. We also temporarily stop tags if there is
+ * a change in transfer negotiation settings to allow
+ * "tag-less" negotiation.
+ */
+ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0
+ || (device->inq_flags & SID_CmdQue) != 0)
+ device_tagenb = TRUE;
+ else
+ device_tagenb = FALSE;
+
+ if (((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0
+ && device_tagenb == FALSE)
+ || ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) == 0
+ && device_tagenb == TRUE)) {
+
+ if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) {
+ /*
+ * Delay change to use tags until after a
+ * few commands have gone to this device so
+ * the controller has time to perform transfer
+ * negotiations without tagged messages getting
+ * in the way.
+ */
+ device->tag_delay_count = CAM_TAG_DELAY_COUNT;
+ device->flags |= CAM_DEV_TAG_AFTER_COUNT;
+ } else {
+ struct ccb_relsim crs;
+
+ xpt_freeze_devq(cts->ccb_h.path, /*count*/1);
+ device->inq_flags &= ~SID_CmdQue;
+ xpt_dev_ccbq_resize(cts->ccb_h.path,
+ sim->max_dev_openings);
+ device->flags &= ~CAM_DEV_TAG_AFTER_COUNT;
+ device->tag_delay_count = 0;
+
+ xpt_setup_ccb(&crs.ccb_h, cts->ccb_h.path,
+ /*priority*/1);
+ crs.ccb_h.func_code = XPT_REL_SIMQ;
+ crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY;
+ crs.openings
+ = crs.release_timeout
+ = crs.qfrozen_cnt
+ = 0;
+ xpt_action((union ccb *)&crs);
+ }
+ }
+ }
+ if (async_update == FALSE)
+ (*(sim->sim_action))(sim, (union ccb *)cts);
+}
+
+#else /* CAM_NEW_TRAN_CODE */
+
static void
xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
int async_update)
@@ -5972,6 +6527,9 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
}
}
+
+#endif /* CAM_NEW_TRAN_CODE */
+
static void
xpt_toggle_tags(struct cam_path *path)
{
@@ -5991,11 +6549,24 @@ xpt_toggle_tags(struct cam_path *path)
struct ccb_trans_settings cts;
xpt_setup_ccb(&cts.ccb_h, path, 1);
+#ifdef CAM_NEW_TRAN_CODE
+ cts.protocol = PROTO_SCSI;
+ cts.protocol_version = PROTO_VERSION_UNSPECIFIED;
+ cts.transport = XPORT_UNSPECIFIED;
+ cts.transport_version = XPORT_VERSION_UNSPECIFIED;
+ cts.proto_specific.scsi.flags = 0;
+ cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ;
+#else /* CAM_NEW_TRAN_CODE */
cts.flags = 0;
cts.valid = CCB_TRANS_TQ_VALID;
+#endif /* CAM_NEW_TRAN_CODE */
xpt_set_transfer_settings(&cts, path->device,
/*async_update*/TRUE);
+#ifdef CAM_NEW_TRAN_CODE
+ cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB;
+#else /* CAM_NEW_TRAN_CODE */
cts.flags = CCB_TRANS_TAG_ENB;
+#endif /* CAM_NEW_TRAN_CODE */
xpt_set_transfer_settings(&cts, path->device,
/*async_update*/TRUE);
}
@@ -6111,7 +6682,9 @@ xptconfigfunc(struct cam_eb *bus, void *arg)
static void
xpt_config(void *arg)
{
- /* Now that interrupts are enabled, go find our devices */
+ /*
+ * Now that interrupts are enabled, go find our devices
+ */
#ifdef CAMDEBUG
/* Setup debugging flags and path */
@@ -6252,6 +6825,12 @@ xptaction(struct cam_sim *sim, union ccb *work_ccb)
cpi->unit_number = sim->unit_number;
cpi->bus_id = sim->bus_id;
cpi->base_transfer_speed = 0;
+#ifdef CAM_NEW_TRAN_CODE
+ cpi->protocol = PROTO_UNSPECIFIED;
+ cpi->protocol_version = PROTO_VERSION_UNSPECIFIED;
+ cpi->transport = XPORT_UNSPECIFIED;
+ cpi->transport_version = XPORT_VERSION_UNSPECIFIED;
+#endif /* CAM_NEW_TRAN_CODE */
cpi->ccb_h.status = CAM_REQ_CMP;
xpt_done(work_ccb);
break;
@@ -6330,7 +6909,8 @@ camisr(void *V_queue)
ccb_h->path->bus->sim->devq->send_openings++;
splx(s);
- if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0
+ if (((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0
+ && (ccb_h->status&CAM_STATUS_MASK) != CAM_REQUEUE_REQ)
|| ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0
&& (dev->ccbq.dev_active == 0))) {
OpenPOWER on IntegriCloud