summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1999-05-22 21:58:47 +0000
committergibbs <gibbs@FreeBSD.org>1999-05-22 21:58:47 +0000
commit95dc85b099eb4f732b322f090ce0abad1280fbfd (patch)
tree84ee71641db91cb247029bd5aa2d5bffd89b16dd /sys/cam
parente4dc7e4c0e6535e2f3849d8c0335cbd9e6f86379 (diff)
downloadFreeBSD-src-95dc85b099eb4f732b322f090ce0abad1280fbfd.zip
FreeBSD-src-95dc85b099eb4f732b322f090ce0abad1280fbfd.tar.gz
Add the XPT_PATH_STATS and XPT_GDEV_STATS function codes. These ccb
types allow the reporting of error counts and other statistics. Currently we provide information on the last BDR or bus reset as well as active transaction inforamtion, but this will be expanded as more information is added to aid in error recovery. Use the 'last reset' information to better handle bus settle delays. Peripheral drivers now control whether a bus settle delay occurs and for how long. This allows target mode peripheral drivers to avoid having their device queue frozen by the XPT for what shoudl only be initiator type behavior. Don't perform a bus reset if the target device is incapable of performing transfer negotiation (e.g. Fiber Channel). If we don't perform a bus reset but the controller is capable of transfer negotiations, force negotiations on the first transaction to go to the device. This ensures that we aren't tripped up by a left over negotiation from the prom, BIOS, loader, etc. Add a default async handler funstion to cam_periph.c to remove duplicated code in all initiator type peripheral drivers. Allow mapping of XPT_CONT_TARGET_IO ccbs from userland. They are itentical to XPT_SCSI_IO ccbs as far as data mapping is concerned.
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/cam_ccb.h33
-rw-r--r--sys/cam/cam_periph.c78
-rw-r--r--sys/cam/cam_periph.h10
-rw-r--r--sys/cam/cam_xpt.c273
4 files changed, 280 insertions, 114 deletions
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index 67b5934..a45d62a 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -25,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: cam_ccb.h,v 1.4 1999/03/05 23:13:20 gibbs Exp $
+ * $Id: cam_ccb.h,v 1.5 1999/05/06 20:15:57 ken Exp $
*/
#ifndef _CAM_CAM_CCB_H
@@ -33,6 +33,7 @@
#include <sys/queue.h>
#include <sys/cdefs.h>
+#include <sys/time.h>
#ifndef KERNEL
#include <sys/callout.h>
#endif
@@ -128,6 +129,10 @@ typedef enum {
/* Get EDT entries matching the given pattern */
XPT_DEBUG = 0x0a,
/* Turn on debugging for a bus, target or lun */
+ XPT_PATH_STATS = 0x0b,
+ /* Path statistics (error counts, etc.) */
+ XPT_GDEV_STATS = 0x0c,
+ /* Device statistics (error counts, etc.) */
/* SCSI Control Functions: 0x10->0x1F */
XPT_ABORT = 0x10,
/* Abort the specified CCB */
@@ -243,6 +248,11 @@ struct ccb_getdev {
u_int8_t serial_num[252];
u_int8_t serial_num_len;
u_int8_t pd_type; /* returned peripheral device type */
+/*
+ * GARBAGE COLLECT
+ * Moved to ccb_getdevstats but left here for binary compatibility.
+ * Remove in next rev of CAM version.
+ */
int dev_openings; /* Space left for more work on device*/
int dev_active; /* Transactions running on the device */
int devq_openings;/* Space left for more queued work */
@@ -258,6 +268,19 @@ struct ccb_getdev {
int mintags;
};
+/* Device Statistics CCB */
+struct ccb_getdevstats {
+ struct ccb_hdr ccb_h;
+ int dev_openings; /* Space left for more work on device*/
+ int dev_active; /* Transactions running on the device */
+ int devq_openings; /* Space left for more queued work */
+ int devq_queued; /* Transactions queued to be sent */
+ int held; /*
+ * CCBs held by peripheral drivers
+ * for this device
+ */
+ struct timeval last_reset; /* Time of last bus reset/loop init */
+};
typedef enum {
CAM_GDEVLIST_LAST_DEVICE,
@@ -488,6 +511,12 @@ struct ccb_pathinq {
u_int32_t base_transfer_speed;/* Base bus speed in KB/sec */
};
+/* Path Statistics CCB */
+struct ccb_pathstats {
+ struct ccb_hdr ccb_h;
+ struct timeval last_reset; /* Time of last bus reset/loop init */
+};
+
typedef union {
u_int8_t *sense_ptr; /*
* Pointer to storage
@@ -757,6 +786,8 @@ union ccb {
struct ccb_relsim crs;
struct ccb_setasync csa;
struct ccb_setdev csd;
+ struct ccb_pathstats cpis;
+ struct ccb_getdevstats cgds;
struct ccb_dev_match cdm;
struct ccb_trans_settings cts;
struct ccb_calc_geometry ccg;
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index d365a12..f88373b 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: cam_periph.c,v 1.12 1999/04/19 21:26:07 gibbs Exp $
+ * $Id: cam_periph.c,v 1.13 1999/05/09 01:25:04 ken Exp $
*/
#include <sys/param.h>
@@ -519,6 +519,7 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo)
}
break;
case XPT_SCSI_IO:
+ case XPT_CONT_TARGET_IO:
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE)
return(0);
@@ -834,6 +835,17 @@ cam_periph_runccb(union ccb *ccb,
return(error);
}
+void
+cam_freeze_devq(struct cam_path *path)
+{
+ struct ccb_hdr ccb_h;
+
+ xpt_setup_ccb(&ccb_h, path, /*priority*/1);
+ ccb_h.func_code = XPT_NOOP;
+ ccb_h.flags = CAM_DEV_QFREEZE;
+ xpt_action((union ccb *)&ccb_h);
+}
+
u_int32_t
cam_release_devq(struct cam_path *path, u_int32_t relsim_flags,
u_int32_t openings, u_int32_t timeout,
@@ -1015,6 +1027,70 @@ camperiphdone(struct cam_periph *periph, union ccb *done_ccb)
}
/*
+ * Generic Async Event handler. Peripheral drivers usually
+ * filter out the events that require personal attention,
+ * and leave the rest to this function.
+ */
+void
+cam_periph_async(struct cam_periph *periph, u_int32_t code,
+ struct cam_path *path, void *arg)
+{
+ switch (code) {
+ case AC_LOST_DEVICE:
+ cam_periph_invalidate(periph);
+ break;
+ case AC_SENT_BDR:
+ case AC_BUS_RESET:
+ {
+ cam_periph_bus_settle(periph, SCSI_DELAY);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void
+cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle)
+{
+ struct ccb_getdevstats cgds;
+
+ xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1);
+ cgds.ccb_h.func_code = XPT_GDEV_STATS;
+ xpt_action((union ccb *)&cgds);
+ cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle);
+}
+
+void
+cam_periph_freeze_after_event(struct cam_periph *periph,
+ struct timeval* event_time, u_int duration_ms)
+{
+ struct timeval delta;
+ struct timeval duration_tv;
+ int s;
+
+ s = splclock();
+ microtime(&delta);
+ splx(s);
+ timevalsub(&delta, event_time);
+ duration_tv.tv_sec = duration_ms / 1000;
+ duration_tv.tv_usec = (duration_ms % 1000) * 1000;
+ if (timevalcmp(&delta, &duration_tv, <)) {
+ timevalsub(&duration_tv, &delta);
+
+ duration_ms = duration_tv.tv_sec * 1000;
+ duration_ms += duration_tv.tv_usec / 1000;
+ cam_freeze_devq(periph->path);
+ cam_release_devq(periph->path,
+ RELSIM_RELEASE_AFTER_TIMEOUT,
+ /*reduction*/0,
+ /*timeout*/duration_ms,
+ /*getcount_only*/0);
+ }
+
+}
+
+/*
* Generic error handler. Peripheral drivers usually filter
* out the errors that they handle in a unique mannor, then
* call this function.
diff --git a/sys/cam/cam_periph.h b/sys/cam/cam_periph.h
index 9bad491..2efee41 100644
--- a/sys/cam/cam_periph.h
+++ b/sys/cam/cam_periph.h
@@ -25,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: cam_periph.h,v 1.2 1998/10/13 21:41:32 ken Exp $
+ * $Id: cam_periph.h,v 1.3 1998/10/22 22:16:48 ken Exp $
*/
#ifndef _CAM_CAM_PERIPH_H
@@ -130,9 +130,17 @@ int cam_periph_ioctl(struct cam_periph *periph, int cmd,
int (*error_routine)(union ccb *ccb,
cam_flags camflags,
u_int32_t sense_flags));
+void cam_freeze_devq(struct cam_path *path);
u_int32_t cam_release_devq(struct cam_path *path, u_int32_t relsim_flags,
u_int32_t opening_reduction, u_int32_t timeout,
int getcount_only);
+void cam_periph_async(struct cam_periph *periph, u_int32_t code,
+ struct cam_path *path, void *arg);
+void cam_periph_bus_settle(struct cam_periph *periph,
+ u_int bus_settle_ms);
+void cam_periph_freeze_after_event(struct cam_periph *periph,
+ struct timeval* event_time,
+ u_int duration_ms);
int cam_periph_error(union ccb *ccb, cam_flags camflags,
u_int32_t sense_flags, union ccb *save_ccb);
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index 4f27724..abd91fa 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: cam_xpt.c,v 1.57 1999/05/11 15:44:39 mjacob Exp $
+ * $Id: cam_xpt.c,v 1.58 1999/05/18 00:41:05 gibbs Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
@@ -34,6 +34,7 @@
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/kernel.h>
+#include <sys/time.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/md5.h>
@@ -61,7 +62,6 @@
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_pass.h>
#include "opt_cam.h"
-#include "opt_scsi.h"
/* Datastructures internal to the xpt layer */
@@ -89,30 +89,6 @@ static STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq;
#define CAM_MAX_HIGHPOWER 4
#endif
-/*
- * This is the number of seconds we wait for devices to settle after a SCSI
- * bus reset.
- */
-#ifndef SCSI_DELAY
-#define SCSI_DELAY 2000
-#endif
-/*
- * If someone sets this to 0, we assume that they want the minimum
- * allowable bus settle delay. All devices need _some_ sort of bus settle
- * delay, so we'll set it to a minimum value of 100ms.
- */
-#if (SCSI_DELAY == 0)
-#undef SCSI_DELAY
-#define SCSI_DELAY 100
-#endif
-
-/*
- * Make sure the user isn't using seconds instead of milliseconds.
- */
-#if (SCSI_DELAY < 100)
-#error "SCSI_DELAY is in milliseconds, not seconds! Please use a larger value"
-#endif
-
/* number of high powered commands that can go through right now */
static int num_highpower = CAM_MAX_HIGHPOWER;
@@ -166,6 +142,7 @@ struct cam_ed {
#define CAM_DEV_REL_ON_QUEUE_EMPTY 0x08
#define CAM_DEV_RESIZE_QUEUE_NEEDED 0x10
#define CAM_DEV_TAG_AFTER_COUNT 0x20
+#define CAM_DEV_INQUIRY_DATA_VALID 0x40
u_int32_t tag_delay_count;
#define CAM_TAG_DELAY_COUNT 5
u_int32_t refcount;
@@ -185,6 +162,7 @@ struct cam_et {
target_id_t target_id;
u_int32_t refcount;
u_int generation;
+ struct timeval last_reset;
};
/*
@@ -197,6 +175,7 @@ struct cam_eb {
TAILQ_ENTRY(cam_eb) links;
path_id_t path_id;
struct cam_sim *sim;
+ struct timeval last_reset;
u_int32_t flags;
#define CAM_EB_RUNQ_SCHEDULED 0x01
u_int32_t refcount;
@@ -498,6 +477,9 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
},
};
+static const int xpt_quirk_table_size =
+ sizeof(xpt_quirk_table) / sizeof(*xpt_quirk_table);
+
typedef enum {
DM_RET_COPY = 0x01,
DM_RET_FLAG_MASK = 0x0f,
@@ -744,6 +726,7 @@ static cam_status proberegister(struct cam_periph *periph,
void *arg);
static void probeschedule(struct cam_periph *probe_periph);
static void probestart(struct cam_periph *periph, union ccb *start_ccb);
+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);
@@ -2843,7 +2826,6 @@ xpt_action(union ccb *start_ccb)
case XPT_IMMED_NOTIFY:
case XPT_NOTIFY_ACK:
case XPT_GET_TRAN_SETTINGS:
- case XPT_PATH_INQ:
case XPT_RESET_BUS:
{
struct cam_sim *sim;
@@ -2852,21 +2834,36 @@ xpt_action(union ccb *start_ccb)
(*(sim->sim_action))(sim, start_ccb);
break;
}
+ case XPT_PATH_INQ:
+ {
+ struct cam_sim *sim;
+
+ sim = start_ccb->ccb_h.path->bus->sim;
+ (*(sim->sim_action))(sim, start_ccb);
+ break;
+ }
+ case XPT_PATH_STATS:
+ start_ccb->cpis.last_reset =
+ start_ccb->ccb_h.path->bus->last_reset;
+ start_ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
case XPT_GDEV_TYPE:
{
+ struct cam_ed *dev;
int s;
+ dev = start_ccb->ccb_h.path->device;
s = splcam();
- if ((start_ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) != 0) {
+ if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) {
start_ccb->ccb_h.status = CAM_DEV_NOT_THERE;
} else {
struct ccb_getdev *cgd;
+ struct cam_eb *bus;
struct cam_et *tar;
- struct cam_ed *dev;
cgd = &start_ccb->cgd;
+ bus = cgd->ccb_h.path->bus;
tar = cgd->ccb_h.path->target;
- dev = cgd->ccb_h.path->device;
cgd->inq_data = dev->inq_data;
cgd->pd_type = SID_TYPE(&dev->inq_data);
cgd->dev_openings = dev->ccbq.dev_openings;
@@ -2886,6 +2883,36 @@ xpt_action(union ccb *start_ccb)
splx(s);
break;
}
+ case XPT_GDEV_STATS:
+ {
+ struct cam_ed *dev;
+ int s;
+
+ dev = start_ccb->ccb_h.path->device;
+ s = splcam();
+ if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) {
+ start_ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ } else {
+ struct ccb_getdevstats *cgds;
+ struct cam_eb *bus;
+ struct cam_et *tar;
+
+ cgds = &start_ccb->cgds;
+ bus = cgds->ccb_h.path->bus;
+ tar = cgds->ccb_h.path->target;
+ cgds->dev_openings = dev->ccbq.dev_openings;
+ cgds->dev_active = dev->ccbq.dev_active;
+ cgds->devq_openings = dev->ccbq.devq_openings;
+ cgds->devq_queued = dev->ccbq.queue.entries;
+ cgds->held = dev->ccbq.held;
+ cgds->last_reset = tar->last_reset;
+ if (timevalcmp(&tar->last_reset, &bus->last_reset, <))
+ cgds->last_reset = bus->last_reset;
+ cgds->ccb_h.status = CAM_REQ_CMP;
+ }
+ splx(s);
+ break;
+ }
case XPT_GDEVLIST:
{
struct cam_periph *nperiph;
@@ -3237,6 +3264,8 @@ xpt_action(union ccb *start_ccb)
break;
}
case XPT_NOOP:
+ if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0)
+ xpt_freeze_devq(start_ccb->ccb_h.path, 1);
start_ccb->ccb_h.status = CAM_REQ_CMP;
break;
default:
@@ -3944,6 +3973,7 @@ xpt_bus_register(struct cam_sim *sim, u_int32_t bus)
new_bus->path_id = sim->path_id;
new_bus->sim = sim;
TAILQ_INIT(&new_bus->et_entries);
+ timevalclear(&new_bus->last_reset);
new_bus->refcount = 1; /* Held until a bus_deregister event */
s = splcam();
TAILQ_INSERT_TAIL(&xpt_busses, new_bus, links);
@@ -4059,30 +4089,13 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
bus = path->bus;
- /*
- * Freeze the SIM queue for SCSI_DELAY ms to
- * allow the bus to settle.
- */
if (async_code == AC_BUS_RESET) {
- struct cam_sim *sim;
-
- sim = bus->sim;
-
- /*
- * If there isn't already another timeout pending, go ahead
- * and freeze the simq and set the timeout flag. If there
- * is another timeout pending, replace it with this
- * timeout. There could be two bus reset async broadcasts
- * sent for some dual-channel controllers.
- */
- if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) == 0) {
- xpt_freeze_simq(sim, 1);
- sim->flags |= CAM_SIM_REL_TIMEOUT_PENDING;
- } else
- untimeout(xpt_release_simq_timeout, sim, sim->c_handle);
+ int s;
- sim->c_handle = timeout(xpt_release_simq_timeout,
- sim, (SCSI_DELAY * hz) / 1000);
+ s = splclock();
+ /* Update our notion of when the last reset occurred */
+ microtime(&bus->last_reset);
+ splx(s);
}
for (target = TAILQ_FIRST(&bus->et_entries);
@@ -4092,9 +4105,18 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
next_target = TAILQ_NEXT(target, links);
if (path->target != target
- && path->target != NULL)
+ && path->target->target_id != CAM_TARGET_WILDCARD)
continue;
+ if (async_code == AC_SENT_BDR) {
+ int s;
+
+ /* Update our notion of when the last reset occurred */
+ s = splclock();
+ microtime(&path->target->last_reset);
+ splx(s);
+ }
+
for (device = TAILQ_FIRST(&target->ed_entries);
device != NULL;
device = next_device) {
@@ -4104,7 +4126,7 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
next_device = TAILQ_NEXT(device, links);
if (path->device != device
- && path->device != NULL)
+ && path->device->lun_id != CAM_LUN_WILDCARD)
continue;
/*
@@ -4131,38 +4153,7 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
|| async_code == AC_BUS_RESET)
xpt_toggle_tags(&newpath);
- /*
- * If we send a BDR, freeze the device queue
- * for SCSI_DELAY ms to allow it to settle
- * down.
- */
- if (async_code == AC_SENT_BDR) {
- xpt_freeze_devq(&newpath, 1);
- /*
- * Although this looks bad, it
- * isn't as bad as it seems. We're
- * passing in a stack-allocated path
- * that we then immediately release
- * after scheduling a timeout to
- * release the device queue. So
- * the path won't be around when
- * the timeout fires, right? Right.
- * But it doesn't matter, since
- * xpt_release_devq and its timeout
- * function both take the device as
- * an argument. Theoretically, the
- * device will still be there when
- * the timeout fires, even though
- * the path will be gone.
- */
- cam_release_devq(
- &newpath,
- /*relsim_flags*/
- RELSIM_RELEASE_AFTER_TIMEOUT,
- /*reduction*/0,
- /*timeout*/SCSI_DELAY,
- /*getcount_only*/0);
- } else if (async_code == AC_INQ_CHANGED) {
+ if (async_code == AC_INQ_CHANGED) {
/*
* We've sent a start unit command, or
* something similar to a device that
@@ -4491,6 +4482,7 @@ xpt_alloc_target(struct cam_eb *bus, target_id_t target_id)
*/
bus->refcount++;
TAILQ_INIT(&target->ed_entries);
+ timevalclear(&target->last_reset);
/* Insertion sort into our bus's target list */
cur_target = TAILQ_FIRST(&bus->et_entries);
@@ -4552,6 +4544,7 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
callout_handle_init(&device->c_handle);
device->refcount = 1;
device->flags |= CAM_DEV_UNCONFIGURED;
+ device->quirk = &xpt_quirk_table[xpt_quirk_table_size - 1];
cam_init_pinfo(&device->alloc_ccb_entry.pinfo);
device->alloc_ccb_entry.device = device;
@@ -5038,18 +5031,17 @@ xptscandone(struct cam_periph *periph, union ccb *done_ccb)
static cam_status
proberegister(struct cam_periph *periph, void *arg)
{
- struct ccb_getdev *cgd;
+ union ccb *request_ccb; /* CCB representing the probe request */
probe_softc *softc;
- union ccb *ccb;
- cgd = (struct ccb_getdev *)arg;
+ request_ccb = (union ccb *)arg;
if (periph == NULL) {
printf("proberegister: periph was NULL!!\n");
return(CAM_REQ_CMP_ERR);
}
- if (cgd == NULL) {
- printf("proberegister: no getdev CCB, can't register device\n");
+ if (request_ccb == NULL) {
+ printf("proberegister: no probe CCB, can't register device\n");
return(CAM_REQ_CMP_ERR);
}
@@ -5060,12 +5052,18 @@ proberegister(struct cam_periph *periph, void *arg)
"Unable to allocate softc\n");
return(CAM_REQ_CMP_ERR);
}
- ccb = (union ccb *)cgd;
TAILQ_INIT(&softc->request_ccbs);
- TAILQ_INSERT_TAIL(&softc->request_ccbs, &ccb->ccb_h, periph_links.tqe);
+ TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h,
+ periph_links.tqe);
softc->flags = 0;
periph->softc = softc;
cam_periph_acquire(periph);
+ /*
+ * Ensure we've waited at least a bus settle
+ * delay before attempting to probe the device.
+ */
+ cam_periph_freeze_after_event(periph, &periph->path->bus->last_reset,
+ SCSI_DELAY);
probeschedule(periph);
return(CAM_REQ_CMP);
}
@@ -5073,12 +5071,17 @@ proberegister(struct cam_periph *periph, void *arg)
static void
probeschedule(struct cam_periph *periph)
{
+ struct ccb_pathinq cpi;
union ccb *ccb;
probe_softc *softc;
softc = (probe_softc *)periph->softc;
ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs);
+ xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
/*
* If a device has gone away and another device, or the same one,
* is back in the same place, it should have a unit attention
@@ -5090,12 +5093,23 @@ probeschedule(struct cam_periph *periph)
* luns. If you think a device has gone away start your scan from
* lun 0. This will insure that any bogus transfer settings are
* invalidated.
+ *
+ * If we haven't seen the device before and the controller supports
+ * some kind of transfer negotiation, negotiate with the first
+ * sent command if no bus reset was performed at startup. This
+ * ensures that the device is not confused by transfer negotiation
+ * settings left over by loader or BIOS action.
*/
if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) == 0)
- && (ccb->ccb_h.target_lun == 0))
+ && (ccb->ccb_h.target_lun == 0)) {
softc->action = PROBE_TUR;
- else
+ } else if ((cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) != 0
+ && (cpi.hba_misc & PIM_NOBUSRESET) != 0) {
+ proberequestdefaultnegotiation(periph);
softc->action = PROBE_INQUIRY;
+ } else {
+ softc->action = PROBE_INQUIRY;
+ }
if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE)
softc->flags |= PROBE_NO_ANNOUNCE;
@@ -5238,6 +5252,21 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
}
static void
+proberequestdefaultnegotiation(struct cam_periph *periph)
+{
+ struct ccb_trans_settings cts;
+
+ xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1);
+ cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ cts.flags = CCB_TRANS_USER_SETTINGS;
+ xpt_action((union ccb *)&cts);
+ cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ cts.flags &= ~CCB_TRANS_USER_SETTINGS;
+ cts.flags |= CCB_TRANS_CURRENT_SETTINGS;
+ xpt_action((union ccb *)&cts);
+}
+
+static void
probedone(struct cam_periph *periph, union ccb *done_ccb)
{
probe_softc *softc;
@@ -5275,6 +5304,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
u_int8_t periph_qual;
u_int8_t periph_dtype;
+ path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID;
inq_buf = &path->device->inq_data;
periph_qual = SID_QUAL(inq_buf);
@@ -5456,13 +5486,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
* the user settings, and set them as the current
* settings to set the device up.
*/
- done_ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
- done_ccb->cts.flags = CCB_TRANS_USER_SETTINGS;
- xpt_action(done_ccb);
- done_ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
- done_ccb->cts.flags &= ~CCB_TRANS_USER_SETTINGS;
- done_ccb->cts.flags |= CCB_TRANS_CURRENT_SETTINGS;
- xpt_action(done_ccb);
+ proberequestdefaultnegotiation(periph);
xpt_release_ccb(done_ccb);
/*
@@ -5576,8 +5600,8 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
cts->flags &= ~CCB_TRANS_TAG_ENB;
cts->flags |= cur_cts.flags & CCB_TRANS_TAG_ENB;
}
-
- if ((inq_data->flags & SID_Sync) == 0
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0
+ && (inq_data->flags & SID_Sync) == 0)
|| (cpi.hba_inquiry & PI_SDTR_ABLE) == 0) {
/* Force async */
cts->sync_period = 0;
@@ -5586,12 +5610,14 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
switch (cts->bus_width) {
case MSG_EXT_WDTR_BUS_32_BIT:
- if ((inq_data->flags & SID_WBus32) != 0
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0
+ || (inq_data->flags & SID_WBus32) != 0)
&& (cpi.hba_inquiry & PI_WIDE_32) != 0)
break;
/* Fall Through to 16-bit */
case MSG_EXT_WDTR_BUS_16_BIT:
- if ((inq_data->flags & SID_WBus16) != 0
+ if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0
+ || (inq_data->flags & SID_WBus16) != 0)
&& (cpi.hba_inquiry & PI_WIDE_16) != 0) {
cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
break;
@@ -5704,6 +5730,8 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
static void
xpt_toggle_tags(struct cam_path *path)
{
+ struct cam_ed *dev;
+
/*
* Give controllers a chance to renegotiate
* before starting tag operations. We
@@ -5711,8 +5739,10 @@ xpt_toggle_tags(struct cam_path *path)
* which causes the tag enable command delay
* counter to come into effect.
*/
- if ((path->device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0
- || (path->device->inq_flags & SID_CmdQue) != 0) {
+ dev = path->device;
+ if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0
+ || ((dev->inq_flags & SID_CmdQue) != 0
+ && (dev->inq_flags & (SID_Sync|SID_WBus16|SID_WBus32)) != 0)) {
struct ccb_trans_settings cts;
xpt_setup_ccb(&cts.ccb_h, path, 1);
@@ -5752,12 +5782,29 @@ xpt_start_tags(struct cam_path *path)
}
static int busses_to_config;
+static int busses_to_reset;
static int
xptconfigbuscountfunc(struct cam_eb *bus, void *arg)
{
- if (bus->path_id != CAM_XPT_PATH_ID)
+ if (bus->path_id != CAM_XPT_PATH_ID) {
+ struct cam_path path;
+ struct ccb_pathinq cpi;
+ int can_negotiate;
+
busses_to_config++;
+ xpt_compile_path(&path, NULL, bus->path_id,
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+ xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+ can_negotiate = cpi.hba_inquiry;
+ can_negotiate &= (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE);
+ if ((cpi.hba_misc & PIM_NOBUSRESET) == 0
+ && can_negotiate)
+ busses_to_reset++;
+ xpt_release_path(&path);
+ }
return(1);
}
@@ -5770,6 +5817,7 @@ xptconfigfunc(struct cam_eb *bus, void *arg)
if (bus->path_id != CAM_XPT_PATH_ID) {
cam_status status;
+ int can_negotiate;
work_ccb = xpt_alloc_ccb();
if ((status = xpt_create_path(&path, xpt_periph, bus->path_id,
@@ -5794,7 +5842,10 @@ xptconfigfunc(struct cam_eb *bus, void *arg)
return(1);
}
- if ((work_ccb->cpi.hba_misc & PIM_NOBUSRESET) == 0) {
+ can_negotiate = work_ccb->cpi.hba_inquiry;
+ can_negotiate &= (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE);
+ if ((work_ccb->cpi.hba_misc & PIM_NOBUSRESET) == 0
+ && (can_negotiate != 0)) {
xpt_setup_ccb(&work_ccb->ccb_h, path, /*priority*/1);
work_ccb->ccb_h.func_code = XPT_RESET_BUS;
work_ccb->ccb_h.cbfcnp = NULL;
@@ -5850,7 +5901,7 @@ xpt_config(void *arg)
/* Call manually because we don't have any busses */
xpt_finishconfig(xpt_periph, NULL);
} else {
- if (SCSI_DELAY >= 2000) {
+ if (busses_to_reset > 0 && SCSI_DELAY >= 2000) {
printf("Waiting %d seconds for SCSI "
"devices to settle\n", SCSI_DELAY/1000);
}
OpenPOWER on IntegriCloud