On 11/27/2015 7:44 PM, Matthew Grooms wrote:
I spent the day looking over the FreeBSD cam and scsi_da source code. After sprinkling a bunch of printf's around to see what code paths were being called, It's obvious that Edward was correct in assuming that ESXi doesn't return any 'Unit Attention' sense information in response to a 'Read Capacity' request. This kinda makes sense as ESXi emulates SCSI-2 disk devices and, as far as I can tell, the 0x2A/0x09 ASC/ASCQ sense code that denotes 'Capacity Data Has Changed' wasn't defined until the SCSCI-3 spec. It's frustrating that the only way to get the scsci_da code to call reprobe() is by receiving a command from the device. Would something like this work? ...

1) Register a callback using xpt_register_async( daasync, AC_REPROBE_DEVICE, path ) that calls reprobe() 2) Implement a new IOCTL in cam_xpt that camcontrol can call with the bus:target:lun as the argument 3) have cam_xpt capture the IOCTL request and call xpt_async( AC_REPROBE_DEVICE, path ) as a result

This way users would have the option of manually asking cam to communicate the new size to geom. The only option now is one or more reboots to gain access to the increased disk capacity. If this sounds like a reasonable approach, I'll take a stab at implementing it.


Here is a proof of concept patch. I'm a complete noob when it comes to cam, scsi or freebsd kernel development for that matter, so I'm sure it could have been done a better way. In any case, I added a new command to camcontrol that allows you to specify a bus, target and lun as an argument. For example ...

# camcontrol readcap da1 -h
Device Size: 32 G, Block Length: 512 bytes

# gpart show da1
=>      40  58720176  da1  GPT  (28G)
        40  58720176    1  freebsd-ufs  (28G)

Note, I resized the VMDK disk in ESXi. The camcontrol output shows the size as 32G but geom thinks its 28G.

# camcontrol devlist
<NECVMWar VMware IDE CDR10 1.00>   at scbus1 target 0 lun 0 (cd0,pass0)
<VMware Virtual disk 1.0>          at scbus2 target 0 lun 0 (pass1,da0)
<VMware Virtual disk 1.0>          at scbus2 target 1 lun 0 (pass2,da1)
<FREEBSD CTLDISK 0001>             at scbus3 target 0 lun 0 (da2,pass3)

# camcontrol reprobe 2:1:0

This generates an event that is captured by the scsci da device to forces a reprobe. The kernel output looks almost identical to when the 'Unit Attention' sense data is received ...

Nov 28 17:46:13 iscsi-i kernel: (da1:mpt0:0:1:0): Re-probe requested
Nov 28 17:46:13 iscsi-i kernel: GEOM_PART: da1 was automatically resized.
Nov 28 17:46:13 iscsi-i kernel: Use `gpart commit da1` to save changes or `gpart undo da1` to revert them.

Now that geom knows about the increased disk capacity, I can increase the partition size and grow the fs ...

[root@iscsi-i /home/mgrooms]# gpart show da1
=>      40  67108784  da1  GPT  (32G)
        40  58720176    1  freebsd-ufs  (28G)
  58720216   8388608       - free -  (4.0G)

# gpart resize -i 1 da1
da1p1 resized

# growfs da1p1
Device is mounted read-write; resizing will result in temporary write suspension for /var/data1.
It's strongly recommended to make a backup before growing the file system.
OK to grow filesystem on /dev/da1p1, mounted on /var/data1, from 28GB to 32GB? [Yes/No] Yes
super-block backups (for fsck_ffs -b #) at:
 58983232, 60265472, 61547712, 62829952, 64112192, 65394432, 66676672

# df -h
Filesystem    Size    Used   Avail Capacity  Mounted on
/dev/da0p3     18G    5.3G     12G    31%    /
devfs         1.0K    1.0K      0B   100%    /dev
/dev/da1p1     31G     32M     28G     0%    /var/data1
/dev/da2p1     15G     32M     14G     0%    /var/data2

Sure would be nice to have something like this in the tree. It's really a drag to have to reboot production VMs to increase disk capacity when it could be easily avoided. I'm not sure what the correct IOCTL should look like. Maybe CAMIOCOMMAND is a better way to go? If someone with some experience with the cam/scsi subsystems was willing to give me some direction I'd be willing to try and rewrite the patch in a way that would be commit worthy. I just need some direction.

Thanks,

-Matthew
Index: lib/libcam/camlib.c
===================================================================
--- lib/libcam/camlib.c (revision 291390)
+++ lib/libcam/camlib.c (working copy)
@@ -752,3 +752,41 @@
        bcopy(src, dst, sizeof(struct cam_device));
 
 }
+
+/*
+ * Send a reprobe unit request for a given bus, target and lun
+ */
+int
+cam_reprobe_btl(path_id_t path_id, target_id_t target_id, lun_id_t target_lun)
+{
+       int fd;
+       char *func_name = "cam_reprobe_btl";
+       union ccb ccb;
+
+       if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
+               snprintf(cam_errbuf, CAM_ERRBUF_SIZE,
+                        "%s: couldn't open %s\n%s: %s", func_name, XPT_DEVICE,
+                        func_name, strerror(errno));
+               return(-1);
+       }
+
+       /* Setup our request ccb */
+       bzero(&ccb.ccb_h, sizeof(struct ccb_hdr));
+       ccb.ccb_h.path_id = path_id;
+       ccb.ccb_h.target_id = target_id;
+       ccb.ccb_h.target_lun = target_lun;
+
+       /*
+        * Attempt to issue a unit reprobe request.  This ioctl will fail if the
+        * kernel can't find a unit for the given path, target and lun.
+        */
+       if (ioctl(fd, CAMUNITREPROBE, &ccb) != 0) {
+               snprintf(cam_errbuf, CAM_ERRBUF_SIZE, 
+                        "%s: failed to issue unit reprobe request", func_name 
);
+               close(fd);
+               return(-1);
+       }
+
+       close(fd);
+       return 0;
+}
Index: lib/libcam/camlib.h
===================================================================
--- lib/libcam/camlib.h (revision 291390)
+++ lib/libcam/camlib.h (working copy)
@@ -146,6 +146,8 @@
                                        struct cam_device *dst);
 int                    cam_get_device(const char *path, char *dev_name,
                                       int devnamelen, int *unit);
+int                    cam_reprobe_btl(path_id_t path_id, target_id_t 
target_id,
+                               lun_id_t target_lun);
 
 /*
  * Buffer encoding/decoding routines, from the old SCSI library.
Index: sbin/camcontrol/camcontrol.c
===================================================================
--- sbin/camcontrol/camcontrol.c        (revision 291390)
+++ sbin/camcontrol/camcontrol.c        (working copy)
@@ -100,7 +100,8 @@
        CAM_CMD_APM             = 0x00000021,
        CAM_CMD_AAM             = 0x00000022,
        CAM_CMD_ATTRIB          = 0x00000023,
-       CAM_CMD_OPCODES         = 0x00000024
+       CAM_CMD_OPCODES         = 0x00000024,
+       CAM_CMD_REPROBE         = 0x00000025
 } cam_cmdmask;
 
 typedef enum {
@@ -193,6 +194,7 @@
 #endif /* MINIMALISTIC */
        {"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
        {"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
+       {"reprobe", CAM_CMD_REPROBE, CAM_ARG_NONE, NULL},
 #ifndef MINIMALISTIC
        {"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
        {"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
@@ -3127,6 +3129,34 @@
 }
 
 static int
+doreprobe(int argc, char **argv)
+{
+       static const char must[] =
+               "you must specify a bus:target:lun to reprobe";
+       int rv;
+       path_id_t bus = CAM_BUS_WILDCARD;
+       target_id_t target = CAM_TARGET_WILDCARD;
+       lun_id_t lun = CAM_LUN_WILDCARD;
+       char *tstr;
+
+       if (argc < 3) {
+               warnx(must);
+               return(1);
+       }
+
+       tstr = argv[optind];
+       while (isspace(*tstr) && (*tstr != '\0'))
+               tstr++;
+       rv = parse_btl(argv[optind], &bus, &target, &lun, &arglist);
+       if (rv != 3) {
+               warnx(must);
+               return(1);
+       }
+
+       return(cam_reprobe_btl(bus, target, lun));
+}
+
+static int
 rescan_or_reset_bus(path_id_t bus, int rescan)
 {
        union ccb ccb, matchccb;
@@ -8685,6 +8715,7 @@
 #endif /* MINIMALISTIC */
 "        camcontrol rescan     <all | bus[:target:lun]>\n"
 "        camcontrol reset      <all | bus[:target:lun]>\n"
+"        camcontrol reprobe    bus:target:lun\n"
 #ifndef MINIMALISTIC
 "        camcontrol defects    [dev_id][generic args] <-f format> [-P][-G]\n"
 "                              [-q][-s][-S offset][-X]\n"
@@ -9053,6 +9084,7 @@
         */
        if ((cmdlist == CAM_CMD_RESCAN)
         || (cmdlist == CAM_CMD_RESET)
+        || (cmdlist == CAM_CMD_REPROBE)
         || (cmdlist == CAM_CMD_DEVTREE)
         || (cmdlist == CAM_CMD_USAGE)
         || (cmdlist == CAM_CMD_DEBUG))
@@ -9204,6 +9236,9 @@
                case CAM_CMD_RESET:
                        error = dorescan_or_reset(argc, argv, 0);
                        break;
+               case CAM_CMD_REPROBE:
+                       error = doreprobe(argc, argv);
+                       break;
 #ifndef MINIMALISTIC
                case CAM_CMD_READ_DEFECTS:
                        error = readdefects(cam_dev, argc, argv, combinedopt,
Index: sys/amd64/conf/GENERIC
===================================================================
--- sys/amd64/conf/GENERIC      (revision 291390)
+++ sys/amd64/conf/GENERIC      (working copy)
@@ -366,3 +366,6 @@
 
 # The crypto framework is required by IPSEC
 device         crypto                  # Required by IPSEC
+
+# Cam Debug Support
+options                CAMDEBUG
Index: sys/cam/cam_ccb.h
===================================================================
--- sys/cam/cam_ccb.h   (revision 291390)
+++ sys/cam/cam_ccb.h   (working copy)
@@ -767,6 +767,7 @@
  * Definitions for the asynchronous callback CCB fields.
  */
 typedef enum {
+       AC_UNIT_REPROBE         = 0x8000,/* Device reprobe user request */
        AC_UNIT_ATTENTION       = 0x4000,/* Device reported UNIT ATTENTION */
        AC_ADVINFO_CHANGED      = 0x2000,/* Advance info might have changes */
        AC_CONTRACT             = 0x1000,/* A contractual callback */
Index: sys/cam/cam_xpt.c
===================================================================
--- sys/cam/cam_xpt.c   (revision 291390)
+++ sys/cam/cam_xpt.c   (working copy)
@@ -717,7 +717,41 @@
                }
                xpt_unlock_buses();
                break;
+       }
+       case CAMUNITREPROBE: {
+               union ccb *inccb;
+               struct cam_path path;
+
+               inccb = (union ccb *)addr;
+
+               /*
+                * Make sure a specific bus, target and lun were specified.
+                */
+               if (inccb->ccb_h.path_id == CAM_BUS_WILDCARD ||
+                       inccb->ccb_h.target_id == CAM_TARGET_WILDCARD ||
+                   inccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+                       error = EINVAL;
+                       break;
                }
+
+               /*
+                * Compile a path using the bus, target, and lun the user 
passed in.
+                */
+               if (xpt_compile_path(&path, NULL,
+                                   inccb->ccb_h.path_id,
+                                   inccb->ccb_h.target_id,
+                                   inccb->ccb_h.target_lun) !=
+                                   CAM_REQ_CMP){
+                       error = EINVAL;
+                       break;
+               }
+
+               /*
+                * Notify interested parties
+                */
+               xpt_async(AC_UNIT_REPROBE, &path, NULL);
+               break;
+       }
        default:
                error = ENOTTY;
                break;
Index: sys/cam/scsi/scsi_da.c
===================================================================
--- sys/cam/scsi/scsi_da.c      (revision 291390)
+++ sys/cam/scsi/scsi_da.c      (working copy)
@@ -1702,6 +1702,17 @@
                }
                break;
        }
+       case AC_UNIT_REPROBE:
+       {
+               softc = (struct da_softc *)periph->softc;
+
+               xpt_print(periph->path,
+                   "Re-probe requested\n");
+               softc->flags &= ~DA_FLAG_PROBED;
+               dareprobe(periph);
+
+               break;
+       }
        case AC_UNIT_ATTENTION:
        {
                union ccb *ccb;
@@ -2240,7 +2251,7 @@
         * would be to not attach the device on failure.
         */
        xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
-           AC_ADVINFO_CHANGED | AC_SCSI_AEN | AC_UNIT_ATTENTION,
+           AC_ADVINFO_CHANGED | AC_SCSI_AEN | AC_UNIT_ATTENTION | 
AC_UNIT_REPROBE,
            daasync, periph, periph->path);
 
        /*
@@ -3244,12 +3255,11 @@
                                        if (have_sense)
                                                scsi_sense_print(
                                                        &done_ccb->csio);
-                                       else {
+                                       else
                                                xpt_print(periph->path,
                                                    "got CAM status %#x\n",
                                                    done_ccb->ccb_h.status);
-                                       }
-
+                                       
                                        xpt_print(periph->path, "fatal error, "
                                            "failed to attach to device\n");
 
Index: sys/cam/scsi/scsi_pass.h
===================================================================
--- sys/cam/scsi/scsi_pass.h    (revision 291390)
+++ sys/cam/scsi/scsi_pass.h    (working copy)
@@ -38,5 +38,6 @@
  */
 #define CAMIOCOMMAND   _IOWR(CAM_VERSION, 2, union ccb)
 #define CAMGETPASSTHRU _IOWR(CAM_VERSION, 3, union ccb)
+#define CAMUNITREPROBE _IOWR(CAM_VERSION, 4, union ccb)
 
 #endif
_______________________________________________
freebsd-current@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "freebsd-current-unsubscr...@freebsd.org"

Reply via email to