@@ -41,9 +41,9 @@ static char *sg_version_date = "20190520";
#include <linux/atomic.h>
#include <linux/ratelimit.h>
#include <linux/uio.h>
-#include <linux/cred.h> /* for sg_check_file_access() */
+#include <linux/cred.h> /* for sg_check_file_access() */
#include <linux/bsg.h>
-#include <linux/proc_fs.h>
+#include <linux/proc_fs.h> /* used if CONFIG_SCSI_PROC_FS */
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
@@ -120,7 +120,9 @@ enum sg_rq_state {
#define SG_FFD_CMD_Q 1 /* clear: only 1 active req per fd */
#define SG_FFD_KEEP_ORPHAN 2 /* policy for this fd */
#define SG_FFD_MMAP_CALLED 3 /* mmap(2) system call made on fd */
+#define SG_FFD_TIME_IN_NS 4 /* set: time in nanoseconds, else ms */
#define SG_FFD_Q_AT_TAIL 5 /* set: queue reqs at tail of blk q */
+#define SG_FFD_NO_DURATION 9 /* don't do command duration calc */
int sg_big_buff = SG_DEF_RESERVED_SIZE;
@@ -271,13 +273,12 @@ static void sg_rq_end_io(struct request *rq, blk_status_t status);
static int sg_proc_init(void);
static int sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp,
int dxfer_dir);
-// static int sg_finish_rem_req(struct sg_request *srp);
static void sg_finish_scsi_blk_rq(struct sg_request *srp);
static int sg_mk_sgat(struct sg_request *srp, struct sg_fd *sfp, int minlen);
static int sg_v3_submit(struct file *filp, struct sg_fd *sfp,
struct sg_io_hdr *hp, bool sync,
struct sg_request **o_srp);
-static struct sg_request *sg_common_write(struct sg_comm_wr_t *cwp);
+static struct sg_request *sg_common_write(struct sg_comm_wr_t *cwrp);
static int sg_rd_append(struct sg_request *srp, void __user *outp,
int num_xfer);
static void sg_remove_sgat(struct sg_request *srp);
@@ -302,6 +303,7 @@ static void sg_rep_rq_state_fail(struct sg_device *sdp,
#define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */
#define SZ_SG_IO_V4 ((int)sizeof(struct sg_io_v4)) /* v4 header (in bsg.h) */
#define SZ_SG_REQ_INFO ((int)sizeof(struct sg_req_info))
+#define SZ_SG_EXTENDED_INFO ((int)sizeof(struct sg_extended_info))
/* There is a assert that SZ_SG_IO_V4 >= SZ_SG_IO_HDR in first function */
@@ -440,11 +442,11 @@ static int
sg_open(struct inode *inode, struct file *filp)
{
bool o_excl, non_block;
+ int res;
int min_dev = iminor(inode);
int op_flags = filp->f_flags;
struct sg_device *sdp;
struct sg_fd *sfp;
- int res;
nonseekable_open(inode, filp);
o_excl = !!(op_flags & O_EXCL);
@@ -508,7 +510,6 @@ sg_open(struct inode *inode, struct file *filp)
filp->private_data = sfp;
sfp->tid = (current ? current->pid : -1);
- sfp->tid = (current ? current->pid : -1);
atomic_inc(&sdp->open_cnt);
mutex_unlock(&sdp->open_rel_lock);
@@ -842,7 +843,10 @@ sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp)
is_v4h = test_bit(SG_FRQ_IS_V4I, srp->frq_bm);
sync = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm);
SG_LOG(3, sdp, "%s: is_v4h=%d\n", __func__, (int)is_v4h);
- srp->start_ns = ktime_get_boot_ns();
+ if (test_bit(SG_FFD_NO_DURATION, sfp->ffd_bm))
+ srp->start_ns = 0;
+ else
+ srp->start_ns = ktime_get_boot_ns(); /* assume always > 0 */
srp->duration = 0;
if (!is_v4h && srp->s_hdr3.interface_id == '\0')
@@ -1473,29 +1477,42 @@ sg_calc_sgat_param(struct sg_device *sdp)
sdp->max_sgat_sz = sz;
}
+/*
+ * Returns duration since srp->start_ns (using boot time as an epoch). Unit
+ * is nanoseconds when time_in_ns==true; else it is in milliseconds.
+ * For backward compatibility the duration is placed in a 32 bit unsigned
+ * integer. This limits the maximum nanosecond duration that can be
+ * represented (without wrapping) to about 4.3 seconds. If that is exceeded
+ * return equivalent of 3.999.. secs as it is more eye catching than the real
+ * number. Negative durations should not be possible but if they occur set
+ * duration to an unlikely 2 nanosec. Stalls in a request setup will have
+ * ts0==U64_MAX and will return 1 for an unlikely 1 nanosecond duration.
+ */
static u32
-sg_calc_rq_dur(const struct sg_request *srp)
+sg_calc_rq_dur(const struct sg_request *srp, bool time_in_ns)
{
ktime_t ts0 = srp->start_ns;
ktime_t now_ts;
s64 diff;
- if (ts0 == 0)
+ if (ts0 == 0) /* only when SG_FFD_NO_DURATION is set */
return 0;
if (unlikely(ts0 == U64_MAX)) /* _prior_ to issuing req */
- return 999999999; /* eye catching */
+ return time_in_ns ? 1 : 999999999;
now_ts = ktime_get_boot_ns();
if (unlikely(ts0 > now_ts))
- return 999999998;
- /* unlikely req duration will exceed 2**32 milliseconds */
- diff = ktime_ms_delta(now_ts, ts0);
+ return time_in_ns ? 2 : 999999998;
+ if (time_in_ns)
+ diff = ktime_to_ns(ktime_sub(now_ts, ts0));
+ else /* unlikely req duration will exceed 2**32 milliseconds */
+ diff = ktime_ms_delta(now_ts, ts0);
return (diff > (s64)U32_MAX) ? 3999999999U : (u32)diff;
}
/* Return of U32_MAX means srp is inactive or in slave waiting state */
static u32
sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp,
- bool *is_durp)
+ bool time_in_ns, bool *is_durp)
{
bool is_dur = false;
u32 res = U32_MAX;
@@ -1503,7 +1520,7 @@ sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp,
switch (sr_stp ? *sr_stp : atomic_read(&srp->rq_st)) {
case SG_RS_INFLIGHT:
case SG_RS_BUSY:
- res = sg_calc_rq_dur(srp);
+ res = sg_calc_rq_dur(srp, time_in_ns);
break;
case SG_RS_AWAIT_RD:
case SG_RS_DONE_RD:
@@ -1525,7 +1542,8 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp,
__must_hold(&sfp->rq_list_lock)
{
spin_lock(&srp->req_lck);
- rip->duration = sg_get_dur(srp, NULL, NULL);
+ rip->duration = sg_get_dur(srp, NULL, test_bit(SG_FFD_TIME_IN_NS,
+ sfp->ffd_bm), NULL);
if (rip->duration == U32_MAX)
rip->duration = 0;
rip->orphan = test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm);
@@ -1802,6 +1820,177 @@ sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz)
return res;
}
+/*
+ * Processing of ioctl(SG_SET_GET_EXTENDED(SG_SEIM_CTL_FLAGS)) which is a set
+ * of boolean flags. Access abbreviations: [rw], read-write; [ro], read-only;
+ * [wo], write-only; [raw], read after write; [rbw], read before write.
+ */
+static void
+sg_extended_bool_flags(struct sg_fd *sfp, struct sg_extended_info *seip)
+{
+ bool flg = false;
+ u32 c_flgs_wm = seip->ctl_flags_wr_mask;
+ u32 c_flgs_rm = seip->ctl_flags_rd_mask;
+ u32 *c_flgsp = &seip->ctl_flags;
+ struct sg_device *sdp = sfp->parentdp;
+
+ /* TIME_IN_NS boolean, [raw] time in nanoseconds (def: millisecs) */
+ if (c_flgs_wm & SG_CTL_FLAGM_TIME_IN_NS)
+ assign_bit(SG_FFD_TIME_IN_NS, sfp->ffd_bm,
+ !!(*c_flgsp & SG_CTL_FLAGM_TIME_IN_NS));
+ if (c_flgs_rm & SG_CTL_FLAGM_TIME_IN_NS) {
+ if (test_bit(SG_FFD_TIME_IN_NS, sfp->ffd_bm))
+ *c_flgsp |= SG_CTL_FLAGM_TIME_IN_NS;
+ else
+ *c_flgsp &= ~SG_CTL_FLAGM_TIME_IN_NS;
+ }
+ /* OTHER_OPENS boolean, [ro] any other sg open fds on this dev? */
+ if (c_flgs_rm & SG_CTL_FLAGM_OTHER_OPENS) {
+ if (atomic_read(&sdp->open_cnt) > 1)
+ *c_flgsp |= SG_CTL_FLAGM_OTHER_OPENS;
+ else
+ *c_flgsp &= ~SG_CTL_FLAGM_OTHER_OPENS;
+ }
+ /* Q_TAIL boolean, [raw] 1: queue at tail; 0: head (def: depends) */
+ if (c_flgs_wm & SG_CTL_FLAGM_Q_TAIL)
+ assign_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm,
+ !!(*c_flgsp & SG_CTL_FLAGM_Q_TAIL));
+ if (c_flgs_rm & SG_CTL_FLAGM_Q_TAIL) {
+ if (test_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm))
+ *c_flgsp |= SG_CTL_FLAGM_Q_TAIL;
+ else
+ *c_flgsp &= ~SG_CTL_FLAGM_Q_TAIL;
+ }
+ /* NO_DURATION boolean, [rbw] */
+ if (c_flgs_rm & SG_CTL_FLAGM_NO_DURATION)
+ flg = test_bit(SG_FFD_NO_DURATION, sfp->ffd_bm);
+ if (c_flgs_wm & SG_CTL_FLAGM_NO_DURATION)
+ assign_bit(SG_FFD_NO_DURATION, sfp->ffd_bm,
+ !!(*c_flgsp & SG_CTL_FLAGM_NO_DURATION));
+ if (c_flgs_rm & SG_CTL_FLAGM_NO_DURATION) {
+ if (flg)
+ *c_flgsp |= SG_CTL_FLAGM_NO_DURATION;
+ else
+ *c_flgsp &= ~SG_CTL_FLAGM_NO_DURATION;
+ }
+}
+
+static void
+sg_extended_read_value(struct sg_fd *sfp, struct sg_extended_info *seip)
+{
+ u32 uv;
+ unsigned long iflags;
+ struct sg_fd *a_sfp;
+ struct sg_device *sdp = sfp->parentdp;
+ struct sg_request *srp;
+
+ 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;
+ rcu_read_lock();
+ list_for_each_entry_rcu(srp, &sfp->rq_fl, fl_entry)
+ ++uv;
+ rcu_read_unlock();
+ seip->read_value = uv;
+ break;
+ case SG_SEIRV_DEV_FL_RQS:
+ uv = 0;
+ read_lock_irqsave(&sdp->sfd_llock, iflags);
+ list_for_each_entry(a_sfp, &sdp->sfds, sfd_entry) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(srp, &a_sfp->rq_fl, fl_entry)
+ ++uv;
+ rcu_read_unlock();
+ }
+ read_unlock_irqrestore(&sdp->sfd_llock, iflags);
+ seip->read_value = uv;
+ break;
+ case SG_SEIRV_SUBMITTED: /* counts all non-blocking on active list */
+ seip->read_value = (u32)atomic_read(&sfp->submitted);
+ break;
+ case SG_SEIRV_DEV_SUBMITTED: /* sum(submitted) on all fd's siblings */
+ uv = 0;
+ read_lock_irqsave(&sdp->sfd_llock, iflags);
+ list_for_each_entry(a_sfp, &sdp->sfds, sfd_entry)
+ uv += (u32)atomic_read(&a_sfp->submitted);
+ read_unlock_irqrestore(&sdp->sfd_llock, 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;
+ }
+}
+
+/* Called when processing ioctl(SG_SET_GET_EXTENDED) */
+static int
+sg_ctl_extended(struct sg_fd *sfp, void __user *p)
+{
+ int result = 0;
+ int ret = 0;
+ int s_wr_mask, s_rd_mask;
+ u32 or_masks;
+ struct sg_device *sdp = sfp->parentdp;
+ struct sg_extended_info *seip;
+ struct sg_extended_info sei;
+
+ seip = &sei;
+ if (copy_from_user(seip, p, SZ_SG_EXTENDED_INFO))
+ return -EFAULT;
+ s_wr_mask = seip->sei_wr_mask;
+ s_rd_mask = seip->sei_rd_mask;
+ or_masks = s_wr_mask | s_rd_mask;
+ if (or_masks == 0) {
+ SG_LOG(2, sdp, "%s: both masks 0, do nothing\n", __func__);
+ return 0;
+ }
+ SG_LOG(3, sdp, "%s: wr_mask=0x%x rd_mask=0x%x\n", __func__, s_wr_mask,
+ s_rd_mask);
+ /* check all boolean flags for either wr or rd mask set in or_mask */
+ if (or_masks & SG_SEIM_CTL_FLAGS)
+ sg_extended_bool_flags(sfp, seip);
+ /* yields minor_index (type: u32) [ro] */
+ if (or_masks & SG_SEIM_MINOR_INDEX) {
+ if (s_wr_mask & SG_SEIM_MINOR_INDEX) {
+ SG_LOG(2, sdp, "%s: writing to minor_index ignored\n",
+ __func__);
+ }
+ if (s_rd_mask & SG_SEIM_MINOR_INDEX)
+ seip->minor_index = sdp->index;
+ }
+ if ((s_rd_mask & SG_SEIM_READ_VAL) && (s_wr_mask & SG_SEIM_READ_VAL))
+ sg_extended_read_value(sfp, seip);
+ /* reserved_sz [raw], since may be reduced by other limits */
+ if (s_wr_mask & SG_SEIM_RESERVED_SIZE) {
+ mutex_lock(&sfp->f_mutex);
+ result = sg_set_reserved_sz(sfp, (int)seip->reserved_sz);
+ if (ret == 0 && result)
+ ret = result;
+ mutex_unlock(&sfp->f_mutex);
+ }
+ if (s_rd_mask & SG_SEIM_RESERVED_SIZE)
+ seip->reserved_sz = (u32)min_t(int,
+ sfp->rsv_srp->sgat_h.buflen,
+ sdp->max_sgat_sz);
+ /* copy to user space if int or boolean read mask non-zero */
+ if (s_rd_mask || seip->ctl_flags_rd_mask) {
+ if (copy_to_user(p, seip, SZ_SG_EXTENDED_INFO))
+ ret = ret ? ret : -EFAULT;
+ }
+ return ret;
+}
+
/*
* For backward compatibility, output SG_MAX_QUEUE sg_req_info objects. First
* fetch from the active list then, if there is still room, from the free
@@ -1928,6 +2117,9 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
res = sg_ctl_abort(sdp, sfp, p);
mutex_unlock(&sfp->f_mutex);
return res;
+ case SG_SET_GET_EXTENDED:
+ SG_LOG(3, sdp, "%s: SG_SET_GET_EXTENDED\n", __func__);
+ return sg_ctl_extended(sfp, p);
case SG_GET_SCSI_ID:
return sg_ctl_scsi_id(sdev, sdp, p);
case SG_SET_FORCE_PACK_ID:
@@ -2452,7 +2644,9 @@ sg_rq_end_io(struct request *rq, blk_status_t status)
SG_LOG(6, sdp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->pack_id,
srp->rq_result);
- srp->duration = sg_calc_rq_dur(srp);
+ if (srp->start_ns > 0) /* zero only when SG_FFD_NO_DURATION is set */
+ srp->duration = sg_calc_rq_dur(srp, test_bit(SG_FFD_TIME_IN_NS,
+ sfp->ffd_bm));
if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0))
sg_check_sense(sdp, srp, slen);
if (slen > 0) {
@@ -3441,6 +3635,9 @@ sg_add_request(struct sg_comm_wr_t *cwrp, int dxfr_len)
r_srp->frq_bm[0] = cwrp->frq_bm[0]; /* assumes <= 32 req flags */
r_srp->sgat_h.dlen = dxfr_len;/* must be <= r_srp->sgat_h.buflen */
r_srp->cmd_opcode = 0xff; /* set invalid opcode (VS), 0x0 is TUR */
+ /* If setup stalls (e.g. blk_get_request()) debug shows 'elap=1 ns' */
+ if (test_bit(SG_FFD_TIME_IN_NS, fp->ffd_bm))
+ r_srp->start_ns = U64_MAX;
if (mk_new_srp)
spin_lock_irqsave(&fp->rq_list_lock, iflags);
list_add_tail_rcu(&r_srp->rq_entry, &fp->rq_list);
@@ -3543,6 +3740,7 @@ sg_add_sfp(struct sg_device *sdp)
assign_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm, SG_DEF_FORCE_PACK_ID);
assign_bit(SG_FFD_CMD_Q, sfp->ffd_bm, SG_DEF_COMMAND_Q);
assign_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm, SG_DEF_KEEP_ORPHAN);
+ assign_bit(SG_FFD_TIME_IN_NS, sfp->ffd_bm, SG_DEF_TIME_UNIT);
assign_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm, SG_DEFAULT_Q_AT);
atomic_set(&sfp->submitted, 0);
atomic_set(&sfp->waiting, 0);
@@ -3979,7 +4177,8 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
/* Writes debug info for one sg_request in obp buffer */
static int
-sg_proc_dbg_sreq(struct sg_request *srp, int to, char *obp, int len)
+sg_proc_dbg_sreq(struct sg_request *srp, int to, bool t_in_ns, char *obp,
+ int len)
__must_hold(&srp->req_lck)
{
bool is_v3v4, v4, is_dur;
@@ -3987,6 +4186,7 @@ sg_proc_dbg_sreq(struct sg_request *srp, int to, char *obp, int len)
u32 dur;
enum sg_rq_state rq_st;
const char *cp;
+ const char *tp = t_in_ns ? "ns" : "ms";
if (len < 1)
return 0;
@@ -3999,15 +4199,15 @@ sg_proc_dbg_sreq(struct sg_request *srp, int to, char *obp, int len)
cp = (srp->rq_info & SG_INFO_DIRECT_IO_MASK) ?
" dio>> " : " ";
rq_st = atomic_read(&srp->rq_st);
- dur = sg_get_dur(srp, &rq_st, &is_dur);
+ dur = sg_get_dur(srp, &rq_st, t_in_ns, &is_dur);
n += scnprintf(obp + n, len - n, "%s%s: dlen=%d/%d id=%d", cp,
sg_rq_st_str(rq_st, false), srp->sgat_h.dlen,
srp->sgat_h.buflen, (int)srp->pack_id);
if (is_dur) /* cmd/req has completed, waiting for ... */
- n += scnprintf(obp + n, len - n, " dur=%ums", dur);
+ n += scnprintf(obp + n, len - n, " dur=%u%s", dur, tp);
else if (dur < U32_MAX) /* in-flight or busy (so ongoing) */
- n += scnprintf(obp + n, len - n, " t_o/elap=%us/%ums",
- to / 1000, dur);
+ n += scnprintf(obp + n, len - n, " t_o/elap=%us/%u%s",
+ to / 1000, dur, tp);
n += scnprintf(obp + n, len - n, " sgat=%d op=0x%02x\n",
srp->sgat_h.num_sgat, srp->cmd_opcode);
return n;
@@ -4045,7 +4245,9 @@ sg_proc_dbg_fd(struct sg_fd *fp, char *obp, int len)
atomic_read(&fp->waiting), fp->tid);
list_for_each_entry_rcu(srp, &fp->rq_list, rq_entry) {
spin_lock(&srp->req_lck);
- n += sg_proc_dbg_sreq(srp, fp->timeout, obp + n, len - n);
+ n += sg_proc_dbg_sreq(srp, fp->timeout,
+ test_bit(SG_FFD_TIME_IN_NS, fp->ffd_bm),
+ obp + n, len - n);
spin_unlock(&srp->req_lck);
}
if (list_empty(&fp->rq_list))
@@ -4057,7 +4259,9 @@ sg_proc_dbg_fd(struct sg_fd *fp, char *obp, int len)
first_fl = false;
}
spin_lock(&srp->req_lck);
- n += sg_proc_dbg_sreq(srp, fp->timeout, obp + n, len - n);
+ n += sg_proc_dbg_sreq(srp, fp->timeout,
+ test_bit(SG_FFD_TIME_IN_NS, fp->ffd_bm),
+ obp + n, len - n);
spin_unlock(&srp->req_lck);
}
return n;
@@ -154,6 +154,70 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
int unused;
} sg_req_info_t;
+/*
+ * The following defines are for manipulating struct sg_extended_info which
+ * is abbreviated to "SEI". A following "M" (i.e. "_SEIM_") indicates a
+ * mask. Most mask values correspond to a integer (usually a uint32_t) apart
+ * from SG_SEIM_CTL_FLAGS which is for boolean values packed into an integer.
+ * The mask values for those booleans start with "SG_CTL_FLAGM_". The scope
+ * of these settings, like most other ioctls, is usually that of the file
+ * descriptor the ioctl is executed on. The "rd:" indication means read-only,
+ * attempts to write to them are ignored. "rd>" means action when reading.
+ */
+#define SG_SEIM_CTL_FLAGS 0x1 /* ctl_flags_mask bits in ctl_flags */
+#define SG_SEIM_READ_VAL 0x2 /* write SG_SEIRV_*, read back value */
+#define SG_SEIM_RESERVED_SIZE 0x4 /* reserved_sz of reserve request */
+#define SG_SEIM_MINOR_INDEX 0x10 /* sg device minor index number */
+#define SG_SEIM_SGAT_ELEM_SZ 0x80 /* sgat element size (>= PAGE_SIZE) */
+#define SG_SEIM_ALL_BITS 0xff /* should be OR of previous items */
+
+/* flag and mask values for boolean fields follow */
+#define SG_CTL_FLAGM_TIME_IN_NS 0x1 /* time: nanosecs (def: millisecs) */
+#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_NO_DURATION 0x400 /* don't calc command duration */
+#define SG_CTL_FLAGM_ALL_BITS 0xfff /* 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(fl rqs) on all dev's fds */
+#define SG_SEIRV_SUBMITTED 0x5 /* number of mrqs submitted+unread */
+#define SG_SEIRV_DEV_SUBMITTED 0x6 /* sum(submitted) on all dev's fds */
+
+/*
+ * A pointer to the following structure is passed as the third argument to
+ * ioctl(SG_SET_GET_EXTENDED). Each bit in the *_wr_mask fields causes the
+ * corresponding integer (e.g. reserved_sz) or bit (e.g. the
+ * SG_CTL_FLAG_TIME_IN_NS bit in ctl_flags) to be read from the user space
+ * and modify the driver. Each bit in the *_rd_mask fields causes the
+ * corresponding integer or bit to be fetched from the driver and written
+ * back to the user space. If the same bit is set in both the *_wr_mask and
+ * corresponding *_rd_mask fields, then which one comes first depends on the
+ * setting but no other operation will split the two. This structure is
+ * padded to 96 bytes to allow for new values to be added in the future.
+ */
+
+/* If both sei_wr_mask and sei_rd_mask are 0, this ioctl does nothing */
+struct sg_extended_info {
+ __u32 sei_wr_mask; /* OR-ed SG_SEIM_* user->driver values */
+ __u32 sei_rd_mask; /* OR-ed SG_SEIM_* driver->user values */
+ __u32 ctl_flags_wr_mask; /* OR-ed SG_CTL_FLAGM_* values */
+ __u32 ctl_flags_rd_mask; /* OR-ed SG_CTL_FLAGM_* values */
+ __u32 ctl_flags; /* bit values OR-ed, see SG_CTL_FLAGM_* */
+ __u32 read_value; /* write SG_SEIRV_*, read back related */
+
+ __u32 reserved_sz; /* data/sgl size of pre-allocated request */
+ __u32 tot_fd_thresh; /* total data/sgat for this fd, 0: no limit */
+ __u32 minor_index; /* rd: kernel's sg device minor number */
+ __u32 share_fd; /* SHARE_FD and CHG_SHARE_FD use this */
+ __u32 sgat_elem_sz; /* sgat element size (must be power of 2) */
+ __u8 pad_to_96[52]; /* pad so struct is 96 bytes long */
+};
+
/*
* IOCTLs: Those ioctls that are relevant to the SG 3.x drivers follow.
* [Those that only apply to the SG 2.x drivers are at the end of the file.]
@@ -183,6 +247,9 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
*/
#define SG_IOCTL_MAGIC_NUM 0x22
+#define SG_SET_GET_EXTENDED _IOWR(SG_IOCTL_MAGIC_NUM, 0x51, \
+ struct sg_extended_info)
+
/* The following ioctl has a 'sg_scsi_id_t *' object as its 3rd argument. */
#define SG_GET_SCSI_ID 0x2276 /* Yields fd's bus, chan, dev, lun + type */
/* SCSI id information can also be obtained from SCSI_IOCTL_GET_IDLUN */
Add ioctl(SG_SET_GET_EXTENDED) together with its interface: struct sg_extended_info which is 96 bytes long, only half of which is currently used. The "SET_GET" component of the name is to stress data flows towards and back from the ioctl. That ioctl has three sections: one for getting and setting 32 bit quantities, a second section for manipulating boolean (bit) flags, and a final section for reading 32 bit quantities where a well known value is written and the corresponding value is read back. Several settings can be made in one invocation. See the webpage at: http://sg.danny.cz/sg/sg_v40.html specifically the section titled: "13 IOCTLs". Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> --- drivers/scsi/sg.c | 252 +++++++++++++++++++++++++++++++++++++---- include/uapi/scsi/sg.h | 67 +++++++++++ 2 files changed, 295 insertions(+), 24 deletions(-)