diff mbox series

[v3,8/8] sg: user controls for q_at_head, read_value

Message ID 20181026114830.13506-9-dgilbert@interlog.com (mailing list archive)
State Deferred
Headers show
Series sg: major cleanup, remove max_queue limit | expand

Commit Message

Douglas Gilbert Oct. 26, 2018, 11:48 a.m. UTC
Add a SG_SET_GET_EXTENDED ioctl control for whether commands
will be queued_at_head or queued_at_tail by the block layer
(together with the scsi mid-level). It has file scope.

Also add a read_value integer the can be used by write a
value from the SG_SEIRV_* group then the corresponding value
will be returned.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
---

The user can still override the new file scope setting on a
a per command basis with the SG_FLAG_Q_AT_HEAD and
SG_FLAG_Q_AT_TAIL in the sg v3 and v4 structures.

An example of read_value usage is to write the value
SG_SEIRV_FL_RQS to the read_value field. Then after the
SG_SET_GET_EXTENDED ioctl is run, the number of (inactive)
requests currently on this file descriptor's request free
list is placed in the read_value field.

Added in v3 is SG_SEIRV_DEV_FL_RQS which is an expansion of
SG_SEIRV_FL_RQS. SG_SEIRV_DEV_FL_RQS counts free list entries
on all sg file descriptors currently open on the device that
the file descriptor (given to ioctl()) is associated with.

 drivers/scsi/sg.c      | 160 +++++++++++++++++++++++++++++++----------
 include/scsi/sg.h      |  32 ++-------
 include/uapi/scsi/sg.h |  49 ++++++-------
 3 files changed, 150 insertions(+), 91 deletions(-)

Comments

kernel test robot Oct. 26, 2018, 3:39 p.m. UTC | #1
Hi linux-scsi-owner,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on mkp-scsi/for-next]
[also build test ERROR on v4.19 next-20181019]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/linux-scsi-owner-vger-kernel-org/sg-types-and-naming-cleanup/20181026-220008
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
config: i386-randconfig-x014-201842 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/module.h:9,
                    from drivers/scsi/sg.c:22:
   drivers/scsi/sg.c: In function 'init_sg':
>> drivers/scsi/sg.c:2277:3: error: 'sg_version_date' undeclared (first use in this function); did you mean 'sg_version_num'?
      sg_version_date);
      ^
   include/linux/printk.h:315:34: note: in definition of macro 'pr_info'
     printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
                                     ^~~~~~~~~~~
   drivers/scsi/sg.c:2277:3: note: each undeclared identifier is reported only once for each function it appears in
      sg_version_date);
      ^
   include/linux/printk.h:315:34: note: in definition of macro 'pr_info'
     printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
                                     ^~~~~~~~~~~
   drivers/scsi/sg.c: At top level:
   drivers/scsi/sg.c:246:20: warning: 'sg_rq_state_str' used but never defined
    static const char *sg_rq_state_str(enum sg_rq_state rq_state, bool long_str);
                       ^~~~~~~~~~~~~~~

vim +2277 drivers/scsi/sg.c

  2256	
  2257	static int __init
  2258	init_sg(void)
  2259	{
  2260		int rc;
  2261	
  2262		if (scatter_elem_sz < PAGE_SIZE) {
  2263			scatter_elem_sz = PAGE_SIZE;
  2264			scatter_elem_sz_prev = scatter_elem_sz;
  2265		}
  2266		if (def_reserved_size >= 0)
  2267			sg_big_buff = def_reserved_size;
  2268		else
  2269			def_reserved_size = sg_big_buff;
  2270	
  2271		rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), 
  2272					    SG_MAX_DEVS, "sg");
  2273		if (rc)
  2274			return rc;
  2275		pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n",
  2276			"sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR,
> 2277			sg_version_date);
  2278	        sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic");
  2279	        if ( IS_ERR(sg_sysfs_class) ) {
  2280			rc = PTR_ERR(sg_sysfs_class);
  2281			goto err_out;
  2282	        }
  2283		sg_sysfs_valid = 1;
  2284		rc = scsi_register_interface(&sg_interface);
  2285		if (0 == rc) {
  2286	#ifdef CONFIG_SCSI_PROC_FS
  2287			sg_proc_init();
  2288	#endif				/* CONFIG_SCSI_PROC_FS */
  2289			return 0;
  2290		}
  2291		class_destroy(sg_sysfs_class);
  2292	err_out:
  2293		unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS);
  2294		return rc;
  2295	}
  2296	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Oct. 26, 2018, 5:42 p.m. UTC | #2
Hi linux-scsi-owner,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on mkp-scsi/for-next]
[also build test ERROR on v4.19 next-20181019]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/linux-scsi-owner-vger-kernel-org/sg-types-and-naming-cleanup/20181026-220008
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
config: x86_64-randconfig-r0-10262157 (attached as .config)
compiler: gcc-6 (Debian 6.4.0-9) 6.4.0 20171026
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/module.h:9,
                    from drivers/scsi/sg.c:22:
   drivers/scsi/sg.c: In function 'init_sg':
>> drivers/scsi/sg.c:2277:3: error: 'sg_version_date' undeclared (first use in this function)
      sg_version_date);
      ^
   include/linux/printk.h:315:34: note: in definition of macro 'pr_info'
     printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
                                     ^~~~~~~~~~~
   drivers/scsi/sg.c:2277:3: note: each undeclared identifier is reported only once for each function it appears in
      sg_version_date);
      ^
   include/linux/printk.h:315:34: note: in definition of macro 'pr_info'
     printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
                                     ^~~~~~~~~~~
   drivers/scsi/sg.c: At top level:
   drivers/scsi/sg.c:246:20: warning: 'sg_rq_state_str' used but never defined
    static const char *sg_rq_state_str(enum sg_rq_state rq_state, bool long_str);
                       ^~~~~~~~~~~~~~~

vim +/sg_version_date +2277 drivers/scsi/sg.c

  2256	
  2257	static int __init
  2258	init_sg(void)
  2259	{
  2260		int rc;
  2261	
  2262		if (scatter_elem_sz < PAGE_SIZE) {
  2263			scatter_elem_sz = PAGE_SIZE;
  2264			scatter_elem_sz_prev = scatter_elem_sz;
  2265		}
  2266		if (def_reserved_size >= 0)
  2267			sg_big_buff = def_reserved_size;
  2268		else
  2269			def_reserved_size = sg_big_buff;
  2270	
  2271		rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), 
  2272					    SG_MAX_DEVS, "sg");
  2273		if (rc)
  2274			return rc;
  2275		pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n",
  2276			"sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR,
> 2277			sg_version_date);
  2278	        sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic");
  2279	        if ( IS_ERR(sg_sysfs_class) ) {
  2280			rc = PTR_ERR(sg_sysfs_class);
  2281			goto err_out;
  2282	        }
  2283		sg_sysfs_valid = 1;
  2284		rc = scsi_register_interface(&sg_interface);
  2285		if (0 == rc) {
  2286	#ifdef CONFIG_SCSI_PROC_FS
  2287			sg_proc_init();
  2288	#endif				/* CONFIG_SCSI_PROC_FS */
  2289			return 0;
  2290		}
  2291		class_destroy(sg_sysfs_class);
  2292	err_out:
  2293		unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS);
  2294		return rc;
  2295	}
  2296	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Douglas Gilbert Oct. 26, 2018, 5:58 p.m. UTC | #3
On 2018-10-26 12:48 p.m., Douglas Gilbert wrote:
> Add a SG_SET_GET_EXTENDED ioctl control for whether commands
> will be queued_at_head or queued_at_tail by the block layer
> (together with the scsi mid-level). It has file scope.
> 
> Also add a read_value integer the can be used by write a
> value from the SG_SEIRV_* group then the corresponding value
> will be returned.
> 
> Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
> ---

See "v3.5" of this patch to fix the compile error when
CONFIG_SCSI_PROC_FS is not defined in a kernel build.

Doug Gilbert
diff mbox series

Patch

diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 3e89bbd508de..4d6966d40949 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -59,7 +59,7 @@  static int sg_version_num = 30901;	/* 2 digits for each component */
 
 #ifdef CONFIG_SCSI_PROC_FS
 #include <linux/proc_fs.h>
-static char *sg_version_date = "20181019";
+static char *sg_version_date = "20181024";
 
 static int sg_proc_init(void);
 #endif
@@ -95,6 +95,10 @@  enum sg_rq_state {
 #define SG_DEF_TIME_UNIT SG_TIME_UNIT_MS
 #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)
 
+#define SG_FD_Q_AT_TAIL true
+#define SG_FD_Q_AT_HEAD false
+#define SG_DEFAULT_Q_AT SG_FD_Q_AT_HEAD	/* for backward compatibility */
+
 int sg_big_buff = SG_DEF_RESERVED_SIZE;
 /* N.B. This variable is readable and writeable via
    /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer
@@ -187,6 +191,7 @@  struct sg_fd {		/* holds the state of a file descriptor */
 	bool keep_orphan;/* false -> drop (def), true -> keep for read() */
 	bool mmap_called;	/* false -> mmap() never called on this fd */
 	bool time_in_ns;	/* report times in nanoseconds */
+	bool q_at_tail;		/* queue at tail if true, head when false */
 	u8 next_cmd_len;	/* 0: automatic, >0: use on next write() */
 	struct sg_request *reserve_srp;	/* allocate on open(), starts on fl */
 	struct fasync_struct *async_qp;	/* used by asynchronous notification */
@@ -238,7 +243,7 @@  static struct sg_request *sg_add_request(struct sg_fd *sfp, int dxfr_len,
 static void sg_remove_request(struct sg_fd *sfp, struct sg_request *srp);
 static struct sg_device *sg_get_dev(int min_dev);
 static void sg_device_destroy(struct kref *kref);
-static const char *sg_rq_state_str(u8 rq_state, bool long_str);
+static const char *sg_rq_state_str(enum sg_rq_state rq_state, bool long_str);
 static struct sg_request *sg_mk_srp(struct sg_fd *sfp, bool first,
 				    rwlock_t *rwlp, unsigned long *iflagsp);
 
@@ -855,7 +860,7 @@  sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p,
 
 	if (h4p || !hi_p)
 		return ERR_PTR(-EOPNOTSUPP);
-	srp = sg_add_request(sfp, hi_p->dxfer_len, false);
+	srp = sg_add_request(sfp, hi_p->dxfer_len, sync);
 	if (IS_ERR(srp))
 		return srp;
 	srp->header = *hi_p;	/* structure assignment, could memcpy */
@@ -897,9 +902,13 @@  sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p,
 		srp->start_ts = ktime_get_with_offset(TK_OFFS_BOOT);
 	else
 		hp->duration = jiffies_to_msecs(jiffies);
-	/* at tail if v3 or later interface and tail flag set */
-	at_head = !(hp->interface_id != '\0' &&
-		    (SG_FLAG_Q_AT_TAIL & hp->flags));
+
+	if (hp->interface_id == '\0')	/* v1 and v2 interface */
+		at_head = true;		/* backward compatibility */
+	else if (sfp->q_at_tail)  /* cmd flags can override sfd setting */
+		at_head = (hp->flags & SG_FLAG_Q_AT_HEAD);
+	else		/* this sfd is defaulting to head */
+		at_head = !(hp->flags & SG_FLAG_Q_AT_TAIL);
 
 	srp->rq->timeout = timeout;
 	kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */
@@ -1084,30 +1093,30 @@  sg_reserved_sz(struct sg_fd *sfp, struct sg_extended_info *seip)
 {
 	bool free_n_srp = false;
 	int result = 0;
-	int val, mx_sect_bytes;
+	int new_sz, mx_sect_bytes;
 	unsigned long iflags;
-	struct sg_request *srp;		/* prior reserve request */
+	struct sg_request *o_srp;	/* prior reserve request */
 	struct sg_request *n_srp;	/* new sg_request, may be used */
-	struct sg_request *flf_srp;	/* free list first element */
+	struct sg_request *t_srp;	/* other fl entries */
 	struct sg_device *sdp = sfp->parentdp;
 
 	mx_sect_bytes = max_sectors_bytes(sdp->device->request_queue);
+	o_srp = sfp->reserve_srp;
 	if (!(seip->valid_wr_mask & SG_SEIM_RESERVED_SIZE)) {	/* read only */
-		srp = sfp->reserve_srp;
-		seip->reserved_sz = (u32)min_t(int, srp->data.dlen,
+		seip->reserved_sz = (u32)min_t(int, o_srp->data.dlen,
 					       mx_sect_bytes);
 		SG_LOG(3, sdp, "%s: rd val=%u\n", __func__, seip->reserved_sz);
 		return 0;
 	}
-	val = min_t(int, (int)seip->reserved_sz, mx_sect_bytes);
-	SG_LOG(3, sdp, "%s: val=%u modify to %d\n", __func__,
-	       seip->reserved_sz, val);
+	new_sz = min_t(int, (int)seip->reserved_sz, mx_sect_bytes);
+	SG_LOG(3, sdp, "%s: was %u, modify to %d\n", __func__,
+	       o_srp->data.dlen, new_sz);
 	/* Should sizes less than PAGE_SIZE be permitted? Round up? */
-	n_srp = sg_mk_srp(sfp, true, NULL, NULL);
+	n_srp = sg_mk_srp(sfp, true /* can take time */, NULL, NULL);
 	if (IS_ERR(n_srp))
 		return PTR_ERR(n_srp);
-	if (val > 0) {
-		result = sg_mk_sgat_dlen(n_srp, sfp, val);
+	if (new_sz > 0) {
+		result = sg_mk_sgat_dlen(n_srp, sfp, new_sz);
 		if (result) {
 			kfree(n_srp);
 			return result;
@@ -1115,35 +1124,39 @@  sg_reserved_sz(struct sg_fd *sfp, struct sg_extended_info *seip)
 	}
 	/* new sg_request object, sized correctly is now available */
 	write_lock_irqsave(&sfp->rq_list_lock, iflags);
-	srp = sfp->reserve_srp;
-	spin_lock(&srp->rq_entry_lck);
-	/* Should not matter if srp->rq_state != SG_RQ_INACTIVE */
+	o_srp = sfp->reserve_srp;
+	spin_lock(&o_srp->rq_entry_lck);
 	if (sfp->mmap_called) {
 		result = -EBUSY;
 		free_n_srp = true;
 		goto unlock;
 	}
-	flf_srp = list_first_entry_or_null(&sfp->rq_free_list,
-					   struct sg_request, free_entry);
-	if (flf_srp && flf_srp != srp && val <= flf_srp->data.dlen) {
-		spin_lock(&flf_srp->rq_entry_lck);
-		if (flf_srp->rq_state == SG_RQ_INACTIVE) {
-			free_n_srp = true;
-			sfp->reserve_srp = flf_srp;
+	list_for_each_entry(t_srp, &sfp->rq_free_list, free_entry) {
+		if (t_srp != o_srp && new_sz <= t_srp->data.dlen) {
+			spin_lock(&t_srp->rq_entry_lck);
+			if (t_srp->rq_state == SG_RQ_INACTIVE) {
+				free_n_srp = true;
+				sfp->reserve_srp = t_srp;
+			}
+			spin_unlock(&t_srp->rq_entry_lck);
 		}
-		spin_unlock(&flf_srp->rq_entry_lck);
+		if (free_n_srp)
+			break;
 	}
 	if (!free_n_srp) {
-		sfp->reserve_srp = n_srp;
 		list_add(&n_srp->free_entry, &sfp->rq_free_list);
+		sfp->reserve_srp = n_srp;
 	}
 	if (seip->valid_rd_mask & SG_SEIM_RESERVED_SIZE) {
-		srp = sfp->reserve_srp;
+		struct sg_request *srp = sfp->reserve_srp;
+
 		seip->reserved_sz = (u32)min_t(int, srp->data.dlen,
 					       mx_sect_bytes);
 	}
 unlock:
-	spin_unlock(&srp->rq_entry_lck);
+	if (new_sz > sfp->rem_sgat_thresh)
+		sfp->rem_sgat_thresh = new_sz;
+	spin_unlock(&o_srp->rq_entry_lck);
 	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
 	if (free_n_srp) {
 		sg_remove_sgat(n_srp);
@@ -1190,11 +1203,13 @@  static int
 sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 {
 	int result = 0;
-	u32 uv;
+	unsigned long iflags;
+	u32 uv, or_masks;
 	struct sg_device *sdp = sfp->parentdp;
+	struct sg_fd *a_sfp;
 	struct sg_extended_info *seip;
+	struct sg_request *srp;
 	struct sg_extended_info sei;
-	u32 or_masks;
 
 	seip = &sei;
 	if (!access_ok(VERIFY_READ, p, SZ_SG_EXTENDED_INFO))
@@ -1208,8 +1223,10 @@  sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 	}
 	SG_LOG(3, sdp, "%s: wr_mask=0x%x rd_mask=0x%x\n", __func__,
 	       seip->valid_wr_mask, seip->valid_rd_mask);
+	/* reserved_sz (u32), read-write */
 	if (or_masks & SG_SEIM_RESERVED_SIZE)
 		result = sg_reserved_sz(sfp, seip);
+	/* rq_rem_sgat_threshold (u32), read-write [impacts re-use only] */
 	if (or_masks & SG_SEIM_RQ_REM_THRESH) {
 		if (seip->valid_wr_mask & SG_SEIM_RQ_REM_THRESH) {
 			uv = seip->rq_rem_sgat_thresh;
@@ -1220,6 +1237,7 @@  sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 		if (seip->valid_rd_mask & SG_SEIM_RQ_REM_THRESH)
 			seip->rq_rem_sgat_thresh = sfp->rem_sgat_thresh;
 	}
+	/* tot_fd_thresh (u32), read-write [sum of active cmd dlen_s] */
 	if (or_masks & SG_SEIM_TOT_FD_THRESH) {
 		if (seip->valid_wr_mask & SG_SEIM_TOT_FD_THRESH) {
 			uv = seip->tot_fd_thresh;
@@ -1230,8 +1248,9 @@  sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 		if (seip->valid_rd_mask & SG_SEIM_TOT_FD_THRESH)
 			seip->tot_fd_thresh = sfp->tot_fd_thresh;
 	}
+	/* check all boolean flags if either wr or rd mask set in or_mask */
 	if (or_masks & SG_SEIM_CTL_FLAGS) {
-		/* don't care whether wr or rd mask set in or_mask */
+		/* TIME_IN_NS boolean, read-write */
 		if (seip->ctl_flags_wr_mask & SG_CTL_FLAGM_TIME_IN_NS)
 			sfp->time_in_ns =
 				!!(seip->ctl_flags & SG_CTL_FLAGM_TIME_IN_NS);
@@ -1241,19 +1260,32 @@  sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 			else
 				seip->ctl_flags &= ~SG_CTL_FLAGM_TIME_IN_NS;
 		}
+		/* ORPHANS boolean, read-only */
 		if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_ORPHANS) {
 			if (sg_any_persistent_orphans(sfp))
 				seip->ctl_flags |= SG_CTL_FLAGM_ORPHANS;
 			else
 				seip->ctl_flags &= ~SG_CTL_FLAGM_ORPHANS;
 		}
+		/* OTHER_OPENS boolean, read-only */
 		if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_OTHER_OPENS) {
 			if (sdp->open_cnt > 1)
 				seip->ctl_flags |= SG_CTL_FLAGM_OTHER_OPENS;
 			else
 				seip->ctl_flags &= ~SG_CTL_FLAGM_OTHER_OPENS;
 		}
+		/* Q_TAIL boolean, read-write */
+		if (seip->ctl_flags_wr_mask & SG_CTL_FLAGM_Q_TAIL)
+			sfp->q_at_tail =
+				!!(seip->ctl_flags & SG_CTL_FLAGM_Q_TAIL);
+		if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_Q_TAIL) {
+			if (sfp->q_at_tail)
+				seip->ctl_flags |= SG_CTL_FLAGM_Q_TAIL;
+			else
+				seip->ctl_flags &= ~SG_CTL_FLAGM_Q_TAIL;
+		}
 	}
+	/* minor_index u32, read-only */
 	if (or_masks & SG_SEIM_MINOR_INDEX) {
 		if (seip->valid_wr_mask & SG_SEIM_MINOR_INDEX)
 			SG_LOG(2, sdp, "%s: writing to minor_index ignored\n",
@@ -1261,7 +1293,50 @@  sg_set_get_extended(struct sg_fd *sfp, void __user *p)
 		if (seip->valid_rd_mask & SG_SEIM_MINOR_INDEX)
 			seip->minor_index = sdp->index;
 	}
-	/* send object back to user space if any read mask set */
+	if ((seip->valid_rd_mask & SG_SEIM_READ_VAL) &&
+	    (seip->valid_wr_mask & SG_SEIM_READ_VAL)) {
+		switch (seip->read_value) {
+		case SG_SEIRV_INT_MASK:
+			seip->read_value = SG_SEIM_ALL_BITS;
+			break;
+		case SG_SEIRV_BOOL_MASK:
+			seip->read_value = SG_CTL_FLAGM_ALL_BITS;
+			break;
+		case SG_SEIRV_VERS_NUM:
+			seip->read_value = sg_version_num;
+			break;
+		case SG_SEIRV_FL_RQS:
+			uv = 0;
+			read_lock_irqsave(&sfp->rq_list_lock, iflags);
+			list_for_each_entry(srp, &sfp->rq_free_list,
+					    free_entry)
+				++uv;
+			read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+			seip->read_value = uv;
+			break;
+		case SG_SEIRV_DEV_FL_RQS:
+			uv = 0;
+			read_lock_irqsave(&sdp->sfd_lock, iflags);
+			list_for_each_entry(a_sfp, &sdp->sfds,
+					    sfd_entry) {
+				read_lock(&a_sfp->rq_list_lock);
+				list_for_each_entry(srp, &a_sfp->rq_free_list,
+						    free_entry)
+					++uv;
+				read_unlock(&a_sfp->rq_list_lock);
+			}
+			read_unlock_irqrestore(&sdp->sfd_lock, iflags);
+			seip->read_value = uv;
+			break;
+		default:
+			SG_LOG(6, sdp, "%s: can't decode %d --> read_value\n",
+			       __func__, seip->read_value);
+			seip->read_value = 0;
+			break;
+		}
+	}
+
+	/* finally send object back to user space if any read mask set */
 	if (seip->valid_rd_mask || seip->ctl_flags_rd_mask) {
 		if (access_ok(VERIFY_WRITE, p, SZ_SG_EXTENDED_INFO))
 			result = __copy_to_user(p, seip, SZ_SG_EXTENDED_INFO);
@@ -1318,7 +1393,7 @@  sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
 		mutex_lock(&sfp->f_mutex);
 		result = sg_set_get_extended(sfp, p);
 		mutex_unlock(&sfp->f_mutex);
-		return 0;
+		return result;
 	case SG_SET_TIMEOUT:
 		SG_LOG(3, sdp, "%s:    SG_SET_TIMEOUT\n", __func__);
 		result = get_user(val, ip);
@@ -2197,6 +2272,9 @@  init_sg(void)
 				    SG_MAX_DEVS, "sg");
 	if (rc)
 		return rc;
+	pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n",
+		"sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR,
+		sg_version_date);
         sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic");
         if ( IS_ERR(sg_sysfs_class) ) {
 		rc = PTR_ERR(sg_sysfs_class);
@@ -2592,7 +2670,12 @@  sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id)
 	return NULL;
 }
 
-/* If rwlp and iflagsp non-NULL then release and re-take write lock */
+/*
+ * If 'first' is set then use GFP_KERNEL which may take time but has
+ * improved chance of success, otherwise use GFP_ATOMIC. Only in
+ * 'first' case, if both rwlp and iflagsp are non-NULL then release and
+ * re-take write rwlp.
+ */
 static struct sg_request *
 sg_mk_srp(struct sg_fd *sfp, bool first, rwlock_t *rwlp,
 	  unsigned long *iflagsp)
@@ -2851,6 +2934,7 @@  sg_add_sfp(struct sg_device *sdp)
 				dlen : SG_TOT_FD_THRESHOLD;
 	atomic_set(&sfp->sum_fd_dlens, 0);
 	sfp->time_in_ns = !!SG_DEF_TIME_UNIT;
+	sfp->q_at_tail = SG_DEFAULT_Q_AT;
 	sfp->parentdp = sdp;
 	if (atomic_read(&sdp->detaching)) {
 		kfree(sfp);
@@ -3239,7 +3323,7 @@  sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
 }
 
 static const char *
-sg_rq_state_str(u8 rq_state, bool long_str)
+sg_rq_state_str(enum sg_rq_state rq_state, bool long_str)
 {
 	switch (rq_state) {
 	case SG_RQ_INACTIVE:
diff --git a/include/scsi/sg.h b/include/scsi/sg.h
index 596f68746f66..46fc7cbffd78 100644
--- a/include/scsi/sg.h
+++ b/include/scsi/sg.h
@@ -4,41 +4,21 @@ 
 
 #include <linux/compiler.h>
 
-/*
- * History:
- *  Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user
- *  process control of SCSI devices.
- *  Development Sponsored by Killy Corp. NY NY
- *
- * Original driver (sg.h):
- *   Copyright (C) 1992 Lawrence Foard
- * Version 2 and 3 extensions to driver:
- *   Copyright (C) 1998 - 2018 Douglas Gilbert
- *
- *  Version: 3.9.01 (20181016)
- *  This version is for 2.6, 3 and 4 series kernels.
- *
- * Documentation
- * =============
- * A web site for the SG device driver can be found at:
- *   http://sg.danny.cz/sg  [alternatively check the MAINTAINERS file]
- * The documentation for the sg version 3 driver can be found at:
- *   http://sg.danny.cz/sg/p/sg_v3_ho.html
- * Also see: <kernel_source>/Documentation/scsi/scsi-generic.txt
- *
- * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html
- */
-
 #ifdef __KERNEL__
 extern int sg_big_buff; /* for sysctl */
 #endif
 
+/*
+ * In version 3.9.01 of the sg driver, this file was spilt in two, with the
+ * bulk of the user space interface being placed in the file being included
+ * in the following line.
+ */
 #include <uapi/scsi/sg.h>
 
 #ifdef __KERNEL__
 #define SG_DEFAULT_TIMEOUT_USER (60*USER_HZ) /* HZ == 'jiffies in 1 second' */
 #endif
 
-#undef SG_DEFAULT_TIMEOUT	/* cause define in sg.c */
+#undef SG_DEFAULT_TIMEOUT	/* because of conflicting define in sg.c */
 
 #endif	/* end of ifndef _SCSI_GENERIC_H guard */
diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h
index 37588e1c4e3a..b64d155541ea 100644
--- a/include/uapi/scsi/sg.h
+++ b/include/uapi/scsi/sg.h
@@ -2,27 +2,6 @@ 
 #ifndef _UAPI_SCSI_SG_H
 #define _UAPI_SCSI_SG_H
 
-/*
- * Want to block the original sg.h header from also being included. That
- * causes lots of multiple definition errors. This will only work if this
- * header is included _before_ the original sg.h header .
- */
-#define _SCSI_GENERIC_H		/* original kernel header guard */
-#define _SCSI_SG_H		/* glibc header guard */
-
-/*
- * Other UAPI headers include linux/compiler.h to resolve "__" types but that
- * doesn't always work, perhaps in the future. Fall back to linux/types.h .
- */
-/* #include <linux/compiler.h> */
-#include <linux/types.h>
-#include <linux/major.h>
-
-/* Still problems with __user so define to nothing */
-#define __user
-
-#include <linux/bsg.h>
-
 /*
  * History:
  *  Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user
@@ -48,6 +27,11 @@ 
  * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html
  */
 
+#include <linux/types.h>
+#include <linux/major.h>
+
+/* bsg.h contains the sg v4 use space interface structure. */
+#include <linux/bsg.h>
 
 /*
  * Same structure as used by readv() call. It defines one scatter-gather
@@ -109,7 +93,7 @@  typedef struct sg_io_hdr {
 #define SG_FLAG_MMAP_IO 4	/* request memory mapped IO */
 /* no transfer of kernel buffers to/from user space; to debug indirect IO */
 #define SG_FLAG_NO_DXFER 0x10000
-/* defaults: for sg driver: Q_AT_HEAD; for block layer: Q_AT_TAIL */
+/* defaults: for sg driver (v3): Q_AT_HEAD; for block layer: Q_AT_TAIL */
 #define SG_FLAG_Q_AT_TAIL 0x10
 #define SG_FLAG_Q_AT_HEAD 0x20
 
@@ -186,12 +170,22 @@  typedef struct sg_req_info {	/* used by SG_GET_REQUEST_TABLE ioctl() */
 #define SG_SEIM_TOT_FD_THRESH	0x4	/* tot_fd_thresh field valid */
 #define SG_SEIM_CTL_FLAGS	0x8	/* ctl_flags_mask bits in ctl_flags */
 #define SG_SEIM_MINOR_INDEX	0x10	/* sg device minor index number */
-#define SG_SEIM_ALL_BITS	0x1f	/* should be OR of previous items */
+#define SG_SEIM_READ_VAL	0x20	/* write SG_SEIRV, read related */
+#define SG_SEIM_ALL_BITS	0x3f	/* should be OR of previous items */
 
 #define SG_CTL_FLAGM_TIME_IN_NS	0x1	/* time: nanosecs (def: millisecs) */
 #define SG_CTL_FLAGM_TAG_FOR_PACK_ID	0x2
 #define SG_CTL_FLAGM_OTHER_OPENS	0x4 /* rd: other sg fd_s on this dev */
 #define SG_CTL_FLAGM_ORPHANS	0x8	/* rd: orphaned requests on this fd */
+#define SG_CTL_FLAGM_Q_TAIL	0x10	/* used for future cmds on this fd */
+#define SG_CTL_FLAGM_ALL_BITS	0x1f	/* should be OR of previous items */
+
+/* Write one of the following values to sg_extended_info::read_value, get... */
+#define SG_SEIRV_INT_MASK	0x0	/* get SG_SEIM_ALL_BITS */
+#define SG_SEIRV_BOOL_MASK	0x1	/* get SG_CTL_FLAGM_ALL_BITS */
+#define SG_SEIRV_VERS_NUM	0x2	/* get driver version number as int */
+#define SG_SEIRV_FL_RQS		0x3	/* number of requests in free list */
+#define SG_SEIRV_DEV_FL_RQS	0x4	/* sum of rqs on all fds on this dev */
 
 /*
  * A pointer to the following structure is passed as the third argument to 
@@ -203,11 +197,11 @@  typedef struct sg_req_info {	/* used by SG_GET_REQUEST_TABLE ioctl() */
  * back to the user space. If the same bit is set in both the *_wr_mask and
  * corresponding *_rd_mask fields, then the write action takes place before
  * the read action and no other operation will split the two. This structure
- * is padded to 64 bytes to allow for new values to be added in the future.
+ * is padded to 96 bytes to allow for new values to be added in the future.
  */
 struct sg_extended_info {
-	__u32	valid_wr_mask;	/* OR-ed SG_SEIM_* values */
-	__u32	valid_rd_mask;	/* OR-ed SG_SEIM_* values */
+	__u32	valid_wr_mask;	/* OR-ed SG_SEIM_* user->driver values */
+	__u32	valid_rd_mask;	/* OR-ed SG_SEIM_* driver->user values */
 	__u32	reserved_sz;	/* data/sgl size of pre-allocated request */
 	__u32	rq_rem_sgat_thresh;/* request re-use: clear data/sgat if > */
 	__u32	tot_fd_thresh;	/* total data/sgat for this fd, 0: no limit */
@@ -215,7 +209,8 @@  struct sg_extended_info {
 	__u32	ctl_flags_rd_mask;	/* OR-ed SG_CTL_FLAGM_* values */
 	__u32	ctl_flags;	/* bit values OR-ed, see SG_CTL_FLAGM_* */
 	__u32	minor_index;	/* rd: kernel's sg device minor number */
-	__u8	pad_to_64[28];	/* pad so struct is 64 bytes long */
+	__u32	read_value;	/* write known value, read back related */
+	__u8	pad_to_96[56];	/* pad so struct is 96 bytes long */
 };
 
 /*