*  anything out of the ordinary is seen.
  * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *
- * Copyright (C) 2001 - 2017 Douglas Gilbert
+ * Copyright (C) 2001 - 2018 Douglas Gilbert
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include "scsi_logging.h"
 
 /* make sure inq_product_rev string corresponds to this version */
-#define SDEBUG_VERSION "0187"  /* format to fit INQUIRY revision field */
-static const char *sdebug_version_date = "20171202";
+#define SDEBUG_VERSION "0188"  /* format to fit INQUIRY revision field */
+static const char *sdebug_version_date = "20180128";
 
 #define MY_NAME "scsi_debug"
 
 #define F_INV_OP               0x200
 #define F_FAKE_RW              0x400
 #define F_M_ACCESS             0x800   /* media access */
+#define F_LONG_DELAY           0x1000
 
 #define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR)
 #define FF_MEDIA_IO (F_M_ACCESS | F_FAKE_RW)
        SDEB_I_XDWRITEREAD = 25,        /* 10 only */
        SDEB_I_WRITE_BUFFER = 26,
        SDEB_I_WRITE_SAME = 27,         /* 10, 16 */
-       SDEB_I_SYNC_CACHE = 28,         /* 10 only */
+       SDEB_I_SYNC_CACHE = 28,         /* 10, 16 */
        SDEB_I_COMP_WRITE = 29,
        SDEB_I_LAST_ELEMENT = 30,       /* keep this last (previous + 1) */
 };
 /* 0x80; 0x80->0x9f: 16 byte cdbs */
        0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
        SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0,
-       0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
+       0, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN_16, SDEB_I_SERV_ACT_OUT_16,
 /* 0xa0; 0xa0->0xbf: 12 byte cdbs */
        SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 };
 
+/*
+ * The following "response" functions return the SCSI mid-level's 4 byte
+ * tuple-in-an-int. To handle commands with an IMMED bit, for a faster
+ * command completion, they can mask their return value with
+ * SDEG_RES_IMMED_MASK .
+ */
+#define SDEG_RES_IMMED_MASK 0x40000000
+
 static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *);
 
 /*
  * The following are overflow arrays for cdbs that "hit" the same index in
            {6,  0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 };
 
+static const struct opcode_info_t sync_cache_iarr[] = {
+       {0, 0x91, 0, F_LONG_DELAY | F_M_ACCESS, resp_sync_cache, NULL,
+           {16,  0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} },     /* SYNC_CACHE (16) */
+};
+
 
 /* This array is accessed via SDEB_I_* values. Make sure all are mapped,
  * plus the terminating elements for logic that scans this table such as
        {ARRAY_SIZE(write_iarr), 0x8a, 0, F_D_OUT | FF_MEDIA_IO,
            resp_write_dt0, write_iarr,                 /* WRITE(16) */
                {16,  0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} },         /* WRITE(16) */
-       {0, 0x1b, 0, 0, resp_start_stop, NULL,          /* START STOP UNIT */
+                0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} },
+       {0, 0x1b, 0, F_LONG_DELAY, resp_start_stop, NULL,/* START STOP UNIT */
            {6,  0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
        {ARRAY_SIZE(sa_in_16_iarr), 0x9e, 0x10, F_SA_LOW | F_D_IN,
            resp_readcap16, sa_in_16_iarr, /* SA_IN(16), READ CAPACITY(16) */
            resp_write_same_10, write_same_iarr,        /* WRITE SAME(10) */
                {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0,
                 0, 0, 0, 0, 0} },
-       {0, 0x35, 0, F_DELAY_OVERR | FF_MEDIA_IO, NULL, NULL, /* SYNC_CACHE */
+       {ARRAY_SIZE(sync_cache_iarr), 0x35, 0, F_LONG_DELAY | F_M_ACCESS,
+           resp_sync_cache, sync_cache_iarr,
            {10,  0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
-            0, 0, 0, 0} },
+            0, 0, 0, 0} },                     /* SYNC_CACHE (10) */
        {0, 0x89, 0, F_D_OUT | FF_MEDIA_IO, resp_comp_write, NULL,
            {16,  0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0,
             0, 0xff, 0x3f, 0xc7} },            /* COMPARE AND WRITE */
        }
        stop = !(cmd[4] & 1);
        atomic_xchg(&devip->stopped, stop);
-       return 0;
+       return (cmd[1] & 0x1) ? SDEG_RES_IMMED_MASK : 0; /* check IMMED bit */
 }
 
 static sector_t get_sdebug_capacity(void)
        return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
 }
 
+static int resp_sync_cache(struct scsi_cmnd *scp,
+                          struct sdebug_dev_info *devip)
+{
+       u64 lba;
+       u32 num_blocks;
+       u8 *cmd = scp->cmnd;
+
+       if (cmd[0] == SYNCHRONIZE_CACHE) {      /* 10 byte cdb */
+               lba = get_unaligned_be32(cmd + 2);
+               num_blocks = get_unaligned_be16(cmd + 7);
+       } else {                                /* SYNCHRONIZE_CACHE(16) */
+               lba = get_unaligned_be64(cmd + 2);
+               num_blocks = get_unaligned_be32(cmd + 10);
+       }
+       if (lba + num_blocks > sdebug_capacity) {
+               mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+               return check_condition_result;
+       }
+       return (cmd[1] & 0x2) ? SDEG_RES_IMMED_MASK : 0; /* check IMMED bit */
+}
+
 #define RL_BUCKET_ELEMS 8
 
 /* Even though each pseudo target has a REPORT LUNS "well known logical unit"
                errsts = oip->pfp(scp, devip);  /* calls a resp_* function */
        else if (r_pfp) /* if leaf function ptr NULL, try the root's */
                errsts = r_pfp(scp, devip);
+       if (errsts & SDEG_RES_IMMED_MASK) {
+               errsts &= ~SDEG_RES_IMMED_MASK;
+               flags |= F_DELAY_OVERR;
+               flags &= ~F_LONG_DELAY;
+       }
+
 
 fini:
        if (F_DELAY_OVERR & flags)
                return schedule_resp(scp, devip, errsts, 0, 0);
-       else
+       else if ((sdebug_jdelay || sdebug_ndelay) && (flags & F_LONG_DELAY)) {
+               /*
+                * If any delay is active, want F_LONG_DELAY to be at least 1
+                * second and if sdebug_jdelay>0 want a long delay of that
+                * many seconds.
+                */
+               int jdelay = (sdebug_jdelay < 2) ? 1 : sdebug_jdelay;
+
+               jdelay = mult_frac(USER_HZ * jdelay, HZ, USER_HZ);
+               return schedule_resp(scp, devip, errsts, jdelay, 0);
+       } else
                return schedule_resp(scp, devip, errsts, sdebug_jdelay,
                                     sdebug_ndelay);
 check_cond: