Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 May 2017 20:57:52 +0000 (UTC)
From:      "Kenneth D. Merry" <ken@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r317774 - head/sbin/camcontrol
Message-ID:  <201705032057.v43Kvq7c062133@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ken
Date: Wed May  3 20:57:52 2017
New Revision: 317774
URL: https://svnweb.freebsd.org/changeset/base/317774

Log:
  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
  MFC after:	3 days

Modified:
  head/sbin/camcontrol/camcontrol.8
  head/sbin/camcontrol/camcontrol.c

Modified: head/sbin/camcontrol/camcontrol.8
==============================================================================
--- head/sbin/camcontrol/camcontrol.8	Wed May  3 20:56:54 2017	(r317773)
+++ head/sbin/camcontrol/camcontrol.8	Wed May  3 20:57:52 2017	(r317774)
@@ -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
@@ -578,12 +578,20 @@ start bit cleared and the load/eject bit
 .It Ic rescan
 Tell the kernel to scan all buses 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 buses, 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,
@@ -593,8 +601,8 @@ the disk size visible to the rest of the
 .It Ic reset
 Tell the kernel to reset all buses 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: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c	Wed May  3 20:56:54 2017	(r317773)
+++ head/sbin/camcontrol/camcontrol.c	Wed May  3 20:57:52 2017	(r317774)
@@ -3131,12 +3131,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)
@@ -3146,6 +3241,8 @@ dorescan_or_reset(int argc, char **argv,
 	else
 		error = rescan_or_reset_bus(bus, rescan);
 
+bailout:
+
 	return(error);
 }
 
@@ -8916,8 +9013,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"
@@ -8996,8 +9093,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 buses, the given bus, or bus:target:lun\n"
-"reset       reset all buses, 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?201705032057.v43Kvq7c062133>