@@ -18,6 +18,7 @@
static int sg_version_num = 30901; /* 2 digits for each component */
#define SG_VERSION_STR "3.9.01"
+static char *sg_version_date = "20181024";
#include <linux/module.h>
@@ -59,7 +60,6 @@ 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 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:
@@ -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 */
@@ -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 */
};
/*
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. Change since v3 [patches 1 to 7 of 8 are unchanged, this for 8/8]: - move sg_version_date definition from inside CONFIG_SCSI_PROC_FS conditional out to full file scope and near related definitions drivers/scsi/sg.c | 160 +++++++++++++++++++++++++++++++---------- include/scsi/sg.h | 32 ++------- include/uapi/scsi/sg.h | 49 ++++++------- 3 files changed, 150 insertions(+), 91 deletions(-)