Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 8 May 2017 17:02:03 +0000 (UTC)
From:      "Kenneth D. Merry" <ken@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r317947 - stable/10/sbin/camcontrol
Message-ID:  <201705081702.v48H230l036196@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ken
Date: Mon May  8 17:02:03 2017
New Revision: 317947
URL: https://svnweb.freebsd.org/changeset/base/317947

Log:
  MFC r317774, r317776
  
  r317774:
    Add the ability to rescan or reset devices specified by peripheral
    name and unit number in camcontrol(8).
  
    Previously camcontrol(8) only supported rescanning or resetting
    devices specified by bus:target:lun.  This is because for
    rescanning at least, you don't have a peripheral name and unit
    number (e.g. da4) for devices that don't exist yet.
  
    That is still the case after this change, but in other cases, when
    the device does exist in the CAM EDT (Existing Device Table), we
    do a careful lookup of the bus/target/lun if the user supplies a
    peripheral name and unit number to find the bus:target:lun and then
    issue the requested reset or rescan.
  
    The lookup is done without actually opening the device in question,
    since a rescan is often done to make a device go away after it has
    been pulled.  (This is especially true for busses/controllers, like
    parallel SCSI controllers, that don't automatically detect changes
    in topology.)  Opening a device that is no longer there to
    determine the bus/target/lun might result in error recovery actions
    when the user really just wanted to make the device go away.
  
    sbin/camcontrol/camcontrol.c:
    	In dorescan_or_reset(), if the use hasn't specified a
    	numeric argument, assume he has specified a device.  Lookup
    	the pass(4) instance for that device using the transport
    	layer CAMGETPASSTHRU ioctl.  If that is successful, we can
    	use the returned bus:target:lun to rescan or reset the
    	device.
  
    	Under the hood, resetting a device using XPT_RESET_DEV is
    	actually sent via the pass(4) device anyway.  But this
    	provides a way for the user to specify devices in a more
    	convenient way, and can work on device rescans when the
    	device is going away, assuming it still exists in the EDT.
  
    sbin/camcontrol/camcontrol.8:
    	Update the man page for the rescan and reset subcommands
    	to reflect that you can now use a device name and unit
    	number with them.
  
  Sponsored by:	Spectra Logic
  
  r317776:
    Bump the camcontrol(8) man page date.
  
  Sponsored by:	Spectra Logic

Modified:
  stable/10/sbin/camcontrol/camcontrol.8
  stable/10/sbin/camcontrol/camcontrol.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/10/sbin/camcontrol/camcontrol.8	Mon May  8 17:02:01 2017	(r317946)
+++ stable/10/sbin/camcontrol/camcontrol.8	Mon May  8 17:02:03 2017	(r317947)
@@ -102,10 +102,10 @@
 .Op device id
 .Nm
 .Ic rescan
-.Aq all | bus Ns Op :target:lun
+.Aq all | device id | bus Ns Op :target:lun
 .Nm
 .Ic reset
-.Aq all | bus Ns Op :target:lun
+.Aq all | device id | bus Ns Op :target:lun
 .Nm
 .Ic defects
 .Op device id
@@ -553,12 +553,20 @@ start bit cleared and the load/eject bit
 .It Ic rescan
 Tell the kernel to scan all busses in the system (with the
 .Ar all
-argument), the given bus (XPT_SCAN_BUS), or bus:target:lun
+argument), the given bus (XPT_SCAN_BUS), bus:target:lun or device
 (XPT_SCAN_LUN) for new devices or devices that have gone away.
 The user
 may specify a scan of all busses, a single bus, or a lun.
 Scanning all luns
 on a target is not supported.
+.Pp
+If a device is specified by peripheral name and unit number, for instance
+da4, it may only be rescanned if that device currently exists in the CAM EDT
+(Existing Device Table).
+If the device is no longer there (see
+.Nm
+devlist ),
+you must use the bus:target:lun form to rescan it.
 .It Ic reprobe
 Tell the kernel to refresh the information about the device and
 notify the upper layer,
@@ -568,8 +576,8 @@ the disk size visible to the rest of the
 .It Ic reset
 Tell the kernel to reset all busses in the system (with the
 .Ar all
-argument) or the given bus (XPT_RESET_BUS) by issuing a SCSI bus
-reset for that bus, or to reset the given bus:target:lun
+argument), the given bus (XPT_RESET_BUS) by issuing a SCSI bus
+reset for that bus, or to reset the given bus:target:lun or device
 (XPT_RESET_DEV), typically by issuing a BUS DEVICE RESET message after
 connecting to that device.
 Note that this can have a destructive impact

Modified: stable/10/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/10/sbin/camcontrol/camcontrol.c	Mon May  8 17:02:01 2017	(r317946)
+++ stable/10/sbin/camcontrol/camcontrol.c	Mon May  8 17:02:03 2017	(r317947)
@@ -3126,12 +3126,107 @@ dorescan_or_reset(int argc, char **argv,
 		tstr++;
 	if (strncasecmp(tstr, "all", strlen("all")) == 0)
 		arglist |= CAM_ARG_BUS;
-	else {
+	else if (isdigit(*tstr)) {
 		rv = parse_btl(argv[optind], &bus, &target, &lun, &arglist);
 		if (rv != 1 && rv != 3) {
 			warnx(must, rescan? "rescan" : "reset");
 			return(1);
 		}
+	} else {
+		char name[30];
+		int unit;
+		int fd = -1;
+		union ccb ccb;
+
+		/*
+		 * Note that resetting or rescanning a device used to
+		 * require a bus or bus:target:lun.  This is because the
+		 * device in question may not exist and you're trying to
+		 * get the controller to rescan to find it.  It may also be
+		 * because the device is hung / unresponsive, and opening
+		 * an unresponsive device is not desireable.
+		 *
+		 * It can be more convenient to reference a device by
+		 * peripheral name and unit number, though, and it is
+		 * possible to get the bus:target:lun for devices that
+		 * currently exist in the EDT.  So this can work for
+		 * devices that we want to reset, or devices that exist
+		 * that we want to rescan, but not devices that do not
+		 * exist yet.
+		 *
+		 * So, we are careful here to look up the bus/target/lun
+		 * for the device the user wants to operate on, specified
+		 * by peripheral instance (e.g. da0, pass32) without
+		 * actually opening that device.  The process is similar to
+		 * what cam_lookup_pass() does, except that we don't
+		 * actually open the passthrough driver instance in the end.
+		 */
+
+		if (cam_get_device(tstr, name, sizeof(name), &unit) == -1) {
+			warnx("%s", cam_errbuf);
+			error = 1;
+			goto bailout;
+		}
+
+		if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+			warn("Unable to open %s", XPT_DEVICE);
+			error = 1;
+			goto bailout;
+		}
+
+		bzero(&ccb, sizeof(ccb));
+
+		/*
+		 * The function code isn't strictly necessary for the
+		 * GETPASSTHRU ioctl.
+		 */
+		ccb.ccb_h.func_code = XPT_GDEVLIST;
+
+		/*
+		 * These two are necessary for the GETPASSTHRU ioctl to
+		 * work.
+		 */
+		strlcpy(ccb.cgdl.periph_name, name,
+			sizeof(ccb.cgdl.periph_name));
+		ccb.cgdl.unit_number = unit;
+
+		/*
+		 * Attempt to get the passthrough device.  This ioctl will
+		 * fail if the device name is null, if the device doesn't
+		 * exist, or if the passthrough driver isn't in the kernel.
+		 */
+		if (ioctl(fd, CAMGETPASSTHRU, &ccb) == -1) {
+			warn("Unable to find bus:target:lun for device %s%d",
+			    name, unit);
+			error = 1;
+			close(fd);
+			goto bailout;
+		}
+		if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			const struct cam_status_entry *entry;
+
+			entry = cam_fetch_status_entry(ccb.ccb_h.status);
+			warnx("Unable to find bus:target_lun for device %s%d, "
+			    "CAM status: %s (%#x)", name, unit,
+			    entry ? entry->status_text : "Unknown",
+			    ccb.ccb_h.status);
+			error = 1;
+			close(fd);
+			goto bailout;
+		}
+
+		/*
+		 * The kernel fills in the bus/target/lun.  We don't
+		 * need the passthrough device name and unit number since
+		 * we aren't going to open it.
+		 */
+		bus = ccb.ccb_h.path_id;
+		target = ccb.ccb_h.target_id;
+		lun = ccb.ccb_h.target_lun;
+
+		arglist |= CAM_ARG_BUS | CAM_ARG_TARGET | CAM_ARG_LUN;
+
+		close(fd);
 	}
 
 	if ((arglist & CAM_ARG_BUS)
@@ -3141,6 +3236,8 @@ dorescan_or_reset(int argc, char **argv,
 	else
 		error = rescan_or_reset_bus(bus, rescan);
 
+bailout:
+
 	return(error);
 }
 
@@ -8739,8 +8836,8 @@ usage(int printlong)
 "        camcontrol eject      [dev_id][generic args]\n"
 "        camcontrol reprobe    [dev_id][generic args]\n"
 #endif /* MINIMALISTIC */
-"        camcontrol rescan     <all | bus[:target:lun]>\n"
-"        camcontrol reset      <all | bus[:target:lun]>\n"
+"        camcontrol rescan     <all | bus[:target:lun] | dev_id>\n"
+"        camcontrol reset      <all | bus[:target:lun] | dev_id>\n"
 #ifndef MINIMALISTIC
 "        camcontrol defects    [dev_id][generic args] <-f format> [-P][-G]\n"
 "                              [-q][-s][-S offset][-X]\n"
@@ -8811,8 +8908,8 @@ usage(int printlong)
 "load        send a Start Unit command to the device with the load bit set\n"
 "eject       send a Stop Unit command to the device with the eject bit set\n"
 "reprobe     update capacity information of the given device\n"
-"rescan      rescan all busses, the given bus, or bus:target:lun\n"
-"reset       reset all busses, the given bus, or bus:target:lun\n"
+"rescan      rescan all buses, the given bus, bus:target:lun or device\n"
+"reset       reset all buses, the given bus, bus:target:lun or device\n"
 "defects     read the defect list of the specified device\n"
 "modepage    display or edit (-e) the given mode page\n"
 "cmd         send the given SCSI command, may need -i or -o as well\n"



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201705081702.v48H230l036196>