@@ -141,46 +141,58 @@ struct sg_scatter_hold { /* holding area for scsi scatter gather info */
struct sg_device; /* forward declarations */
struct sg_fd;
-struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
- struct list_head entry; /* list entry */
- struct sg_fd *parentfp; /* NULL -> not in use */
+/*
+ * For any file descriptor: at any time a sg_request object must be a member
+ * of sg_fd::rq_list or rq_free_list::rq_free_list. The only exception is
+ * within a rq_list_lock write lock when it is moving between those two lists.
+ */
+
+struct sg_request { /* active SCSI command or inactive on free list (fl) */
+ struct list_head rq_entry; /* member of rq_list (active cmd) */
+ struct list_head free_entry; /* member of rq_free_list */
+ spinlock_t rq_entry_lck;
struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */
union {
struct sg_io_hdr header; /* see <scsi/sg.h> */
- struct sg_io_v4 hdr_v4; /* see <uapi/linux/bsg.h> */
+ struct sg_v4_hold v4_hold;/* related to <uapi/linux/bsg.h> */
};
- u8 sense_b[SCSI_SENSE_BUFFERSIZE];
- bool hdr_v4_active; /* selector for anonymous union above */
- bool res_used; /* true -> use reserve buffer, false -> don't */
+ ktime_t start_ts; /* used when sg_fd::time_in_ns is true */
+ enum sg_rq_state rq_state;/* tracks lifetime of each request */
+ bool v4_active; /* selector for autonomous union above */
bool orphan; /* true -> drop on sight, false -> normal */
- bool sg_io_owned; /* true -> packet belongs to SG_IO */
- /* done protected by rq_list_lock */
- char done; /* 0->before bh, 1->before read, 2->read */
+ bool sync_invoc;/* true -> synchronous (e.g. from ioctl(SG_IO)) */
+ u8 sense_b[SCSI_SENSE_BUFFERSIZE];
+ struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */
+ struct sg_scatter_hold *d2p; /* optional 2nd data buffer for bidi */
struct request *rq;
struct bio *bio;
- struct execute_work ew;
+ struct execute_work ew_orph; /* harvest orphan request */
};
-struct sg_fd { /* holds the state of a file descriptor */
- struct list_head sfd_siblings; /* protected by device's sfd_lock */
+struct sg_fd { /* holds the state of a file descriptor */
+ struct list_head sfd_entry; /* member sg_device::sfds list */
struct sg_device *parentdp; /* owning device */
wait_queue_head_t read_wait; /* queue read until command done */
- rwlock_t rq_list_lock; /* protect access to list in req_arr */
struct mutex f_mutex; /* protect against changes in this fd */
+ rwlock_t rq_list_lock; /* protect access to sg_request lists */
+ struct list_head rq_list; /* head of inflight sg_request list */
+ struct list_head rq_free_list; /* head of sg_request free list */
int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
- struct sg_scatter_hold reserve; /* one held for this file descriptor */
- struct list_head rq_list; /* head of request list */
- struct fasync_struct *async_qp; /* used by asynchronous notification */
- struct sg_request req_arr[SG_MAX_QUEUE];/* used as singly-linked list */
+ int rem_sgat_thresh; /* > this, request's sgat cleared after use */
+ int tot_fd_thresh; /* E2BIG if sum_of(dlen) > this, 0: ignore */
+ atomic_t sum_fd_dlens; /* when tot_fd_thresh>0 this is sum_of(dlen) */
bool force_packid; /* true -> pack_id input to read() */
bool cmd_q; /* true -> allow command queuing, false -> don't */
- u8 next_cmd_len; /* 0: automatic, >0: use on next write() */
bool keep_orphan;/* false -> drop (def), true -> keep for read() */
bool mmap_called; /* false -> mmap() never called on this fd */
- bool res_in_use; /* true -> 'reserve' array in use */
+ bool sse_seen; /* SG_SET_EXTENDED ioctl seen */
+ bool time_in_ns; /* report times in nanoseconds */
+ 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 */
struct kref f_ref;
- struct execute_work ew;
+ struct execute_work ew; /* harvest all active and free list requests */
};
struct sg_device { /* holds the state of each scsi generic device */
@@ -189,8 +201,8 @@ struct sg_device { /* holds the state of each scsi generic device */
struct mutex open_rel_lock; /* held when in open() or release() */
int sg_tablesize; /* adapter's max scatter-gather table size */
u32 index; /* device index number */
- struct list_head sfds;
- rwlock_t sfd_lock; /* protect access to sfd list */
+ struct list_head sfds; /* head of sg_fd::sfd_entry list */
+ rwlock_t sfd_lock; /* protect access to sfds list */
atomic_t detaching; /* 0->device usable, 1->device detaching */
bool exclude; /* 1->open(O_EXCL) succeeded and is active */
int open_cnt; /* count of opens (perhaps < num(sfds) ) */
@@ -203,36 +215,37 @@ struct sg_device { /* holds the state of each scsi generic device */
/* tasklet or soft irq callback */
static void sg_rq_end_io(struct request *rq, blk_status_t status);
static int sg_start_req(struct sg_request *srp, u8 *cmd);
-static int sg_finish_rem_req(struct sg_request *srp);
-static int sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp,
- int buff_size);
+static void sg_finish_scsi_blk_rq(struct sg_request *srp);
+static int sg_mk_sgat_dlen(struct sg_request *srp, struct sg_fd *sfp,
+ int dlen);
static ssize_t sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count,
struct sg_request *srp);
-static ssize_t sg_new_write(struct sg_fd *sfp, struct file *file,
- const char __user *buf, size_t count, int blocking,
- int read_only, int sg_io_owned,
- struct sg_request **o_srp);
-static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp,
- u8 *cmnd, int timeout, int blocking);
+static ssize_t sg_v3_write(struct sg_fd *sfp, struct file *file,
+ const char __user *buf, size_t count,
+ bool read_only, bool sync,
+ struct sg_request **o_srp);
+static struct sg_request *sg_common_write(struct sg_fd *sfp,
+ const struct sg_io_hdr *hp,
+ struct sg_io_v4 *h4p, u8 *cmnd,
+ bool sync, int timeout);
static int sg_read_oxfer(struct sg_request *srp, char __user *outp,
- int num_read_xfer);
-static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp);
-static void sg_build_reserve(struct sg_fd *sfp, int req_size);
-static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp,
- int size);
-static void sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp);
+ int num_xfer);
+static void sg_remove_sgat(struct sg_request *srp);
static struct sg_fd *sg_add_sfp(struct sg_device *sdp);
static void sg_remove_sfp(struct kref *);
static struct sg_request *sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id);
-static struct sg_request *sg_add_request(struct sg_fd *sfp);
-static int sg_remove_request(struct sg_fd *sfp, struct sg_request *srp);
-static struct sg_device *sg_get_dev(int dev);
+static struct sg_request *sg_add_request(struct sg_fd *sfp, int dxfr_len,
+ bool sync);
+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);
-#define SZ_SG_HEADER sizeof(struct sg_header)
-#define SZ_SG_IO_HDR sizeof(struct sg_io_hdr)
+#define SZ_SG_HEADER sizeof(struct sg_header) /* v1 and v2 header */
+#define SZ_SG_IO_HDR sizeof(struct sg_io_hdr) /* v3 header */
+#define SZ_SG_IO_V4 sizeof(struct sg_io_v4) /* v4 header (in bsg.h) */
/* #define SZ_SG_IOVEC sizeof(struct sg_iovec) synonym for 'struct iovec' */
#define SZ_SG_REQ_INFO sizeof(struct sg_req_info)
+#define SZ_SG_EXTENDED_INFO sizeof(struct sg_extended_info)
/*
* Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages.
@@ -623,7 +636,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
}
} else
count = (ohdr->result == 0) ? 0 : -EIO;
- sg_finish_rem_req(srp);
+ sg_finish_scsi_blk_rq(srp);
sg_remove_request(sfp, srp);
retval = count;
free_old_hdr:
@@ -635,13 +648,13 @@ static ssize_t
sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count,
struct sg_request *srp)
{
- int err = 0, err2;
+ int err = 0;
int len;
struct sg_io_hdr *hp = &srp->header;
if (count < SZ_SG_IO_HDR) {
err = -EINVAL;
- goto err_out;
+ goto out;
}
hp->sb_len_wr = 0;
if ((hp->mx_sb_len > 0) && hp->sbp) {
@@ -654,7 +667,7 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count,
len = (len > sb_len) ? sb_len : len;
if (copy_to_user(hp->sbp, srp->sense_b, len)) {
err = -EFAULT;
- goto err_out;
+ goto out;
}
hp->sb_len_wr = len;
}
@@ -663,27 +676,28 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count,
hp->info |= SG_INFO_CHECK;
if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) {
err = -EFAULT;
- goto err_out;
+ goto out;
}
-err_out:
- err2 = sg_finish_rem_req(srp);
+ if (atomic_read(&sfp->parentdp->detaching))/* okay but on thin ice */
+ hp->info |= SG_INFO_DEVICE_DETACHING;
+out:
+ sg_finish_scsi_blk_rq(srp);
sg_remove_request(sfp, srp);
- return err ? : err2 ? : count;
+ return err ? err : count;
}
static ssize_t
sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
{
- int mxsize, cmd_size, k;
- int input_size, blocking;
+ int mxsize, cmd_size, input_size, retval;
u8 opcode;
struct sg_device *sdp;
struct sg_fd *sfp;
struct sg_request *srp;
- struct sg_io_hdr *hp;
u8 cmnd[SG_MAX_CDB_SIZE];
- int retval;
struct sg_header ohdr;
+ struct sg_io_hdr v3hdr;
+ struct sg_io_hdr *hp = &v3hdr;
retval = sg_check_file_access(filp, __func__);
if (retval)
@@ -710,17 +724,11 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
return -EIO;
if (__copy_from_user(&ohdr, buf, SZ_SG_HEADER))
return -EFAULT;
- blocking = !(filp->f_flags & O_NONBLOCK);
if (ohdr.reply_len < 0)
- return sg_new_write(sfp, filp, buf, count,
- blocking, 0, 0, NULL);
+ return sg_v3_write(sfp, filp, buf, count, false, false, NULL);
if (count < (SZ_SG_HEADER + 6))
return -EIO; /* minimum scsi command length is 6 bytes */
- if (!(srp = sg_add_request(sfp))) {
- SG_LOG(1, sdp, "%s: queue full\n", __func__);
- return -EDOM;
- }
buf += SZ_SG_HEADER;
__get_user(opcode, buf);
mutex_lock(&sfp->f_mutex);
@@ -739,12 +747,10 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
mxsize = (input_size > ohdr.reply_len) ? input_size : ohdr.reply_len;
mxsize -= SZ_SG_HEADER;
input_size -= SZ_SG_HEADER;
- if (input_size < 0) {
- sg_remove_request(sfp, srp);
+ if (input_size < 0)
return -EIO; /* Insufficient bytes passed for this command. */
- }
- hp = &srp->header;
- hp->interface_id = '\0'; /* indicator of old interface tunnelled */
+ memset(hp, 0, sizeof(*hp));
+ hp->interface_id = '\0';/* indicate old interface tunnelled */
hp->cmd_len = (u8)cmd_size;
hp->iovec_count = 0;
hp->mx_sb_len = 0;
@@ -762,111 +768,95 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
hp->dxferp = NULL;
hp->sbp = NULL;
hp->timeout = ohdr.reply_len; /* structure abuse ... */
- hp->flags = input_size; /* structure abuse ... */
+ hp->flags = input_size; /* structure abuse ... */
hp->pack_id = ohdr.pack_id;
hp->usr_ptr = NULL;
if (__copy_from_user(cmnd, buf, cmd_size))
return -EFAULT;
/*
* SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV,
- * but is is possible that the app intended SG_DXFER_TO_DEV, because there
- * is a non-zero input_size, so emit a warning.
+ * but is is possible that the app intended SG_DXFER_TO_DEV, because
+ * there is a non-zero input_size, so emit a warning.
*/
if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) {
printk_ratelimited(KERN_WARNING
- "sg_write: data in/out %d/%d bytes "
- "for SCSI command 0x%x-- guessing "
- "data in;\n program %s not setting "
- "count and/or reply_len properly\n",
- ohdr.reply_len - (int)SZ_SG_HEADER,
- input_size, (unsigned int)cmnd[0],
- current->comm);
- }
- k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
- return (k < 0) ? k : count;
+ "%s: data in/out %d/%d bytes for SCSI command 0x%x-- guessing data in;\n"
+ " program %s not setting count and/or reply_len properly\n",
+ __func__, ohdr.reply_len - (int)SZ_SG_HEADER,
+ input_size, (unsigned int)cmnd[0], current->comm);
+ }
+ srp = sg_common_write(sfp, hp, NULL, cmnd, false, sfp->timeout);
+ return (IS_ERR(srp)) ? PTR_ERR(srp) : count;
}
static ssize_t
-sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf,
- size_t count, int blocking, int read_only, int sg_io_owned,
- struct sg_request **o_srp)
+sg_v3_write(struct sg_fd *sfp, struct file *file, const char __user *buf,
+ size_t count, bool read_only, bool sync,
+ struct sg_request **o_srp)
{
- int k;
- struct sg_request *srp;
- struct sg_io_hdr *hp;
- u8 cmnd[SG_MAX_CDB_SIZE];
+ struct sg_io_hdr v3hdr;
int timeout;
unsigned long ul_timeout;
+ struct sg_io_hdr *hp = &v3hdr;
+ struct sg_request *srp;
+ u8 cmnd[SG_MAX_CDB_SIZE];
if (count < SZ_SG_IO_HDR)
return -EINVAL;
if (!access_ok(VERIFY_READ, buf, count))
- return -EFAULT; /* protects following copy_from_user()s + get_user()s */
-
- sfp->cmd_q = true; /* when sg_io_hdr seen, set command queuing on */
- if (!(srp = sg_add_request(sfp))) {
- SG_LOG(1, sfp->parentdp, "%s: queue full\n", __func__);
- return -EDOM;
- }
- srp->sg_io_owned = sg_io_owned;
- hp = &srp->header;
- if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) {
- sg_remove_request(sfp, srp);
return -EFAULT;
- }
- if (hp->interface_id != 'S') {
- sg_remove_request(sfp, srp);
+ if (__copy_from_user(hp, buf, SZ_SG_IO_HDR))
+ return -EFAULT;
+ if (hp->interface_id == 'Q')
+ return -EOPNOTSUPP; /* placeholder for sgv4 interface */
+ else if (hp->interface_id != 'S')
return -ENOSYS;
- }
if (hp->flags & SG_FLAG_MMAP_IO) {
- if (hp->dxfer_len > sfp->reserve.dlen) {
- sg_remove_request(sfp, srp);
- return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */
- }
- if (hp->flags & SG_FLAG_DIRECT_IO) {
- sg_remove_request(sfp, srp);
- return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */
- }
- if (sfp->res_in_use) {
- sg_remove_request(sfp, srp);
- return -EBUSY; /* reserve buffer already being used */
- }
+ if (!list_empty(&sfp->rq_list))
+ return -EBUSY; /* already active requests on fd */
+ if (hp->dxfer_len > sfp->reserve_srp->data.dlen)
+ return -ENOMEM; /* MMAP_IO size must fit in reserve */
+ if (hp->flags & SG_FLAG_DIRECT_IO)
+ return -EINVAL; /* not both MMAP_IO and DIRECT_IO */
}
- ul_timeout = msecs_to_jiffies(srp->header.timeout);
+ sfp->cmd_q = true; /* when sg_io_hdr seen, set command queuing on */
+ ul_timeout = msecs_to_jiffies(hp->timeout);
+
timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX;
- if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof(cmnd))) {
- sg_remove_request(sfp, srp);
+ if (!hp->cmdp || hp->cmd_len < 6 || hp->cmd_len > sizeof(cmnd))
return -EMSGSIZE;
- }
- if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) {
- sg_remove_request(sfp, srp);
+ if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len))
return -EFAULT; /* protects following copy_from_user()s + get_user()s */
- }
- if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) {
- sg_remove_request(sfp, srp);
+ if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len))
return -EFAULT;
- }
- if (read_only && sg_allow_access(file, cmnd)) {
- sg_remove_request(sfp, srp);
+ if (read_only && sg_allow_access(file, cmnd))
return -EPERM;
- }
- k = sg_common_write(sfp, srp, cmnd, timeout, blocking);
- if (k < 0)
- return k;
+ srp = sg_common_write(sfp, hp, NULL, cmnd, sync, timeout);
+ if (IS_ERR(srp))
+ return PTR_ERR(srp);
if (o_srp)
*o_srp = srp;
return count;
}
-static int
-sg_common_write(struct sg_fd *sfp, struct sg_request *srp,
- u8 *cmnd, int timeout, int blocking)
+
+static struct sg_request *
+sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p,
+ struct sg_io_v4 *h4p, u8 *cmnd, bool sync, int timeout)
{
bool at_head;
- int k;
+ int res;
struct sg_device *sdp = sfp->parentdp;
- struct sg_io_hdr *hp = &srp->header;
+ struct sg_request *srp;
+ struct sg_io_hdr *hp;
+ if (h4p || !hi_p)
+ return ERR_PTR(-EOPNOTSUPP);
+ srp = sg_add_request(sfp, hi_p->dxfer_len, false);
+ if (IS_ERR(srp))
+ return srp;
+ srp->header = *hi_p; /* structure assignment, could memcpy */
+ hp = &srp->header;
srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */
hp->status = 0;
hp->masked_status = 0;
@@ -875,19 +865,18 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp,
hp->host_status = 0;
hp->driver_status = 0;
hp->resid = 0;
- SG_LOG(4, sfp->parentdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n",
- __func__, (int)cmnd[0], (int)hp->cmd_len);
+ SG_LOG(4, sdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__,
+ (int)cmnd[0], (int)hp->cmd_len);
if (hp->dxfer_len >= SZ_256M)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- k = sg_start_req(srp, cmnd);
- if (k) {
- SG_LOG(1, sfp->parentdp, "%s: start_req err=%d\n", __func__,
- k);
- sg_finish_rem_req(srp);
+ res = sg_start_req(srp, cmnd);
+ if (res) {
+ SG_LOG(1, sdp, "%s: start_req err=%d\n", __func__, -res);
+ sg_finish_scsi_blk_rq(srp);
sg_remove_request(sfp, srp);
- return k; /* probably out of space --> ENOMEM */
+ return ERR_PTR(res); /* probably out of space --> ENOMEM */
}
if (atomic_read(&sdp->detaching)) {
if (srp->bio) {
@@ -896,12 +885,15 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp,
srp->rq = NULL;
}
- sg_finish_rem_req(srp);
+ sg_finish_scsi_blk_rq(srp);
sg_remove_request(sfp, srp);
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
- hp->duration = jiffies_to_msecs(jiffies);
+ if (sfp->time_in_ns)
+ 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));
@@ -910,19 +902,8 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp,
kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */
blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk,
srp->rq, (int)at_head, sg_rq_end_io);
- return 0;
-}
-
-static int
-srp_done(struct sg_fd *sfp, struct sg_request *srp)
-{
- unsigned long flags;
- int ret;
-
- read_lock_irqsave(&sfp->rq_list_lock, flags);
- ret = srp->done;
- read_unlock_irqrestore(&sfp->rq_list_lock, flags);
- return ret;
+ /* u32 tag = blk_mq_unique_tag(srp->rq); should now be available */
+ return srp;
}
static int
@@ -935,49 +916,113 @@ max_sectors_bytes(struct request_queue *q)
return max_sectors << 9;
}
+/*
+ * For backward compatibility the duration in nanoseconds is placed in a
+ * 32 bit unsigned integer. This limits the maximum duration that can
+ * be represented (without wrapping) to about 4.3 seconds.
+ */
+static inline u32
+sg_ktime_sub_trunc(ktime_t now_ts, ktime_t ts0)
+{
+ if (ktime_after(now_ts, ts0))
+ return (u32)ktime_to_ns(ktime_sub(now_ts, ts0));
+ else
+ return 0;
+}
+
+/*
+ * Annotation under function arguments (i.e. '__must_hold...') states that
+ * this function expects that lock to be held, a read lock is sufficient in
+ * this case.
+ */
static void
-sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo)
+sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo,
+ int max_num)
+ __must_hold(&sfp->rq_list_lock)
{
struct sg_request *srp;
int val;
- unsigned int ms;
val = 0;
- list_for_each_entry(srp, &sfp->rq_list, entry) {
- if (val >= SG_MAX_QUEUE)
- break;
- rinfo[val].req_state = srp->done + 1;
+ list_for_each_entry(srp, &sfp->rq_list, rq_entry) {
+ if (val >= max_num)
+ return;
+ spin_lock(&srp->rq_entry_lck);
+ rinfo[val].req_state = (int)srp->rq_state;
rinfo[val].problem =
srp->header.masked_status &
srp->header.host_status &
- srp->header.driver_status;
- if (srp->done)
- rinfo[val].duration =
- srp->header.duration;
- else {
- ms = jiffies_to_msecs(jiffies);
- rinfo[val].duration =
- (ms > srp->header.duration) ?
- (ms - srp->header.duration) : 0;
+ srp->header.driver_status;
+ switch (srp->rq_state) {
+ case SG_RQ_INFLIGHT:
+ if (sfp->time_in_ns) {
+ ktime_t now_ts =
+ ktime_get_with_offset(TK_OFFS_BOOT);
+ ktime_t ts0 = srp->start_ts;
+
+ /* N.B. truncation to fit in 32 bit field */
+ rinfo[val].duration =
+ sg_ktime_sub_trunc(now_ts, ts0);
+ } else {
+ unsigned int ms = jiffies_to_msecs(jiffies);
+
+ rinfo[val].duration =
+ (ms > srp->header.duration) ?
+ (ms - srp->header.duration) : 0;
+ }
+ break;
+ case SG_RQ_AWAIT_READ:
+ case SG_RQ_DONE_READ:
+ rinfo[val].duration = srp->header.duration;
+ break;
+ case SG_RQ_INACTIVE:
+ case SG_RQ_BUSY:
+ default:
+ rinfo[val].duration = 0;
+ break;
}
rinfo[val].orphan = srp->orphan;
- rinfo[val].sg_io_owned = srp->sg_io_owned;
+ rinfo[val].sg_io_owned = srp->sync_invoc;
rinfo[val].pack_id = srp->header.pack_id;
rinfo[val].usr_ptr = srp->header.usr_ptr;
+ spin_unlock(&srp->rq_entry_lck);
val++;
}
}
+/*
+ * This function is called from one place: the wait_event_interruptible()
+ * in the synchronous ioctl(SG_IO) call (see sg_ioctl()). Since only one
+ * simple value (a u8) is being read, one argument is that the spinlock
+ * should not be needed. The repercussions of being alerted but not seeing
+ * the new state in srp->rq_state are quite nasty. A middle ground is to
+ * use wait_event_interruptible_lock_irq() .
+ */
+static inline bool
+srp_state_or_detaching(struct sg_device *sdp, struct sg_request *srp)
+{
+ /* unsigned long flags; */
+ bool ret;
+
+ /* spin_lock_irqsave(&srp->rq_entry_lck, flags); */
+ ret = srp->rq_state != SG_RQ_INFLIGHT ||
+ atomic_read(&sdp->detaching);
+ /* spin_unlock_irqrestore(&srp->rq_entry_lck, flags); */
+ return ret;
+}
+
#if 0 /* temporary to shorten big patch */
static long
sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
{
+ bool leave;
void __user *p = (void __user *)arg;
int __user *ip = p;
int result, val, read_only;
struct sg_device *sdp;
struct sg_fd *sfp;
struct sg_request *srp;
+ const char *cp;
unsigned long iflags;
sfp = filp->private_data;
@@ -1251,38 +1296,42 @@ sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
static __poll_t
sg_poll(struct file *filp, poll_table * wait)
{
- __poll_t res = 0;
+ __poll_t pres = 0;
+ bool empty;
+ unsigned long iflags;
struct sg_device *sdp;
struct sg_fd *sfp;
struct sg_request *srp;
- int count = 0;
- unsigned long iflags;
sfp = filp->private_data;
- if (!sfp)
+ if (IS_ERR_OR_NULL(sfp))
return EPOLLERR;
sdp = sfp->parentdp;
- if (!sdp)
+ if (IS_ERR_OR_NULL(sdp))
return EPOLLERR;
poll_wait(filp, &sfp->read_wait, wait);
read_lock_irqsave(&sfp->rq_list_lock, iflags);
- list_for_each_entry(srp, &sfp->rq_list, entry) {
+ empty = list_empty(&sfp->rq_list);
+ list_for_each_entry(srp, &sfp->rq_list, rq_entry) {
/* if any read waiting, flag it */
- if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))
- res = EPOLLIN | EPOLLRDNORM;
- ++count;
+ spin_lock(&srp->rq_entry_lck);
+ if (srp->rq_state == SG_RQ_AWAIT_READ && !srp->sync_invoc) {
+ spin_unlock(&srp->rq_entry_lck);
+ pres = EPOLLIN | EPOLLRDNORM;
+ break;
+ }
+ spin_unlock(&srp->rq_entry_lck);
}
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
if (atomic_read(&sdp->detaching))
- res |= EPOLLHUP;
- else if (!sfp->cmd_q) {
- if (0 == count)
- res |= EPOLLOUT | EPOLLWRNORM;
- } else if (count < SG_MAX_QUEUE)
- res |= EPOLLOUT | EPOLLWRNORM;
- SG_LOG(3, sdp, "%s: res=0x%x\n", __func__, (__force u32)res);
- return res;
+ pres |= EPOLLHUP;
+ else if (sfp->cmd_q)
+ pres |= EPOLLOUT | EPOLLWRNORM;
+ else if (empty)
+ pres |= EPOLLOUT | EPOLLWRNORM;
+ SG_LOG(3, sdp, "%s: pres=0x%x\n", __func__, (__force u32)pres);
+ return pres;
}
static int
@@ -1292,12 +1341,14 @@ sg_fasync(int fd, struct file *filp, int mode)
struct sg_fd *sfp;
sfp = filp->private_data;
- if (!sfp)
- return -ENXIO;
+ if (IS_ERR_OR_NULL(sfp)) {
+ pr_warn("sg: %s: sfp is NULL or error\n", __func__);
+ return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO;
+ }
sdp = sfp->parentdp;
- if (!sdp)
- return -ENXIO;
SG_LOG(3, sdp, "%s: mode=%d\n", __func__, mode);
+ if (IS_ERR_OR_NULL(sdp))
+ return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO;
return fasync_helper(fd, filp, mode, &sfp->async_qp);
}
@@ -1306,25 +1357,46 @@ static vm_fault_t
sg_vma_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
- struct sg_fd *sfp;
- unsigned long offset, len, sa;
struct sg_scatter_hold *rsv_schp;
+ struct sg_request *srp;
+ struct sg_device *sdp;
+ struct sg_fd *sfp;
int k, length;
+ unsigned long offset, len, sa;
+ const char *nbp = "==NULL, bad";
- if (!vma)
+ if (!vma) {
+ pr_warn("%s: vma%s\n", __func__, nbp);
return VM_FAULT_SIGBUS;
+ }
sfp = vma->vm_private_data;
- if (!sfp)
+ if (IS_ERR_OR_NULL(sfp)) {
+ pr_warn("%s: sfp%s\n", __func__, nbp);
return VM_FAULT_SIGBUS;
- rsv_schp = &sfp->reserve;
- offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= rsv_schp->dlen)
+ }
+ sdp = sfp->parentdp;
+ if (sdp && unlikely(atomic_read(&sdp->detaching))) {
+ SG_LOG(1, sdp, "%s: device deatching\n", __func__);
return VM_FAULT_SIGBUS;
- SG_LOG(3, sfp->parentdp, "%s: offset=%lu, scatg=%d\n", __func__,
- offset, rsv_schp->num_sgat);
+ }
+ /* guard against ioctl(SG_SET_RESERVED_SIZE) and the like */
+ mutex_lock(&sfp->f_mutex);
+ srp = sfp->reserve_srp;
+ if (!srp) {
+ SG_LOG(1, sdp, "%s: srp%s\n", __func__, nbp);
+ goto out_err;
+ }
+ rsv_schp = &srp->data;
+ offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= rsv_schp->dlen) {
+ SG_LOG(1, sdp, "%s: offset>reserve.dlen\n", __func__);
+ goto out_err;
+ }
sa = vma->vm_start;
+ SG_LOG(3, sdp, "%s: vm_start=0x%lx, offset=%lu\n", __func__, sa,
+ offset);
length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
- for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) {
+ for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) {
len = vma->vm_end - sa;
len = (len < length) ? len : length;
if (offset < len) {
@@ -1332,12 +1404,14 @@ sg_vma_fault(struct vm_fault *vmf)
offset >> PAGE_SHIFT);
get_page(page); /* increment page count */
vmf->page = page;
+ mutex_unlock(&sfp->f_mutex);
return 0; /* success */
}
sa += len;
offset -= len;
}
-
+out_err:
+ mutex_unlock(&sfp->f_mutex);
return VM_FAULT_SIGBUS;
}
@@ -1348,32 +1422,44 @@ static const struct vm_operations_struct sg_mmap_vm_ops = {
static int
sg_mmap(struct file *filp, struct vm_area_struct *vma)
{
- struct sg_fd *sfp;
- unsigned long req_sz, len, sa;
- struct sg_scatter_hold *rsv_schp;
int k, length;
int ret = 0;
+ unsigned long req_sz, len, sa, iflags;
+ struct sg_scatter_hold *rsv_schp;
+ struct sg_fd *sfp;
+ struct sg_request *srp;
if (!filp || !vma)
return -ENXIO;
sfp = filp->private_data;
- if (!sfp)
- return -ENXIO;
+ if (IS_ERR_OR_NULL(sfp)) {
+ pr_warn("sg: %s: sfp is NULL or error\n", __func__);
+ return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO;
+ }
req_sz = vma->vm_end - vma->vm_start;
- SG_LOG(3, sfp->parentdp, "%s starting, vm_start=%p, len=%d\n",
- __func__, (void *)vma->vm_start, (int)req_sz);
- if (vma->vm_pgoff)
+ SG_LOG(3, sfp->parentdp, "%s: vm_start=%p, len=%d\n", __func__,
+ (void *)vma->vm_start, (int)req_sz);
+ if (vma->vm_pgoff || IS_ERR_OR_NULL(sfp->parentdp))
return -EINVAL; /* want no offset */
- rsv_schp = &sfp->reserve;
+ /*
+ * Assume no requests active on this file descriptor (sfp) so that
+ * the reserve request is on free list
+ */
mutex_lock(&sfp->f_mutex);
+ srp = sfp->reserve_srp;
+ spin_lock_irqsave(&srp->rq_entry_lck, iflags);
+ if (srp->rq_state != SG_RQ_INACTIVE) {
+ ret = -EBUSY;
+ goto out;
+ }
+ rsv_schp = &srp->data;
if (req_sz > rsv_schp->dlen) {
- ret = -ENOMEM; /* cannot map more than reserved buffer */
+ ret = -ENOMEM;
goto out;
}
-
sa = vma->vm_start;
length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
- for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) {
+ for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) {
len = vma->vm_end - sa;
len = (len < length) ? len : length;
sa += len;
@@ -1384,6 +1470,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_private_data = sfp;
vma->vm_ops = &sg_mmap_vm_ops;
out:
+ spin_unlock_irqrestore(&srp->rq_entry_lck, iflags);
mutex_unlock(&sfp->f_mutex);
return ret;
}
@@ -1399,10 +1486,20 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
static void
sg_rq_end_io_usercontext(struct work_struct *work)
{
- struct sg_request *srp = container_of(work, struct sg_request, ew.work);
- struct sg_fd *sfp = srp->parentfp;
+ struct sg_request *srp = container_of(work, struct sg_request,
+ ew_orph.work);
+ struct sg_fd *sfp;
- sg_finish_rem_req(srp);
+ if (!srp) {
+ WARN_ONCE("s: srp unexpectedly NULL\n", __func__);
+ return;
+ }
+ sfp = srp->parentfp;
+ if (!sfp) {
+ WARN_ONCE(1, "%s: sfp unexpectedly NULL\n", __func__);
+ return;
+ }
+ sg_finish_scsi_blk_rq(srp);
sg_remove_request(sfp, srp);
kref_put(&sfp->f_ref, sg_remove_sfp);
}
@@ -1418,36 +1515,46 @@ static void
sg_rq_end_io(struct request *rq, blk_status_t status)
{
struct sg_request *srp = rq->end_io_data;
- struct scsi_request *req = scsi_req(rq);
+ struct scsi_request *scsi_rp = scsi_req(rq);
struct sg_device *sdp;
struct sg_fd *sfp;
- unsigned long iflags;
- unsigned int ms;
u8 *sense;
- int result, resid, done = 1;
+ unsigned long iflags;
+ int result, resid;
+ enum sg_rq_state rqq_state = SG_RQ_AWAIT_READ;
- if (WARN_ON(srp->done != 0))
+ if (WARN_ON(srp->rq_state != SG_RQ_INFLIGHT))
return;
-
sfp = srp->parentfp;
- if (WARN_ON(sfp == NULL))
+ if (unlikely(!sfp)) {
+ WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__);
return;
-
+ }
sdp = sfp->parentdp;
if (unlikely(atomic_read(&sdp->detaching)))
pr_info("%s: device detaching\n", __func__);
- sense = req->sense;
- result = req->result;
- resid = req->resid_len;
+ sense = scsi_rp->sense;
+ result = scsi_rp->result;
+ resid = scsi_rp->resid_len;
SG_LOG(4, sdp, "%s: pack_id=%d, res=0x%x\n", __func__,
srp->header.pack_id, result);
srp->header.resid = resid;
- ms = jiffies_to_msecs(jiffies);
- srp->header.duration = (ms > srp->header.duration) ?
- (ms - srp->header.duration) : 0;
- if (0 != result) {
+ if (sfp->time_in_ns) {
+ ktime_t now_ts = ktime_get_with_offset(TK_OFFS_BOOT);
+ ktime_t ts0 = srp->start_ts;
+
+ /* N.B. truncation to fit in 32 bit field */
+ srp->header.duration = ktime_after(now_ts, ts0) ?
+ (u32)ktime_sub(now_ts, ts0) : 0;
+ } else {
+ unsigned int ms = jiffies_to_msecs(jiffies);
+
+ srp->header.duration = (ms > srp->header.duration) ?
+ (ms - srp->header.duration) : 0;
+ }
+ if (unlikely(result)) {
struct scsi_sense_hdr sshdr;
srp->header.status = 0xff & result;
@@ -1473,8 +1580,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status)
}
}
- if (req->sense_len)
- memcpy(srp->sense_b, req->sense, SCSI_SENSE_BUFFERSIZE);
+ if (scsi_rp->sense_len)
+ memcpy(srp->sense_b, scsi_rp->sense, SCSI_SENSE_BUFFERSIZE);
/* Rely on write phase to clean out srp status values, so no "else" */
@@ -1485,29 +1592,30 @@ sg_rq_end_io(struct request *rq, blk_status_t status)
* blk_rq_unmap_user() can be called from user context.
*/
srp->rq = NULL;
- scsi_req_free_cmd(scsi_req(rq));
+ scsi_req_free_cmd(scsi_rp);
__blk_put_request(rq->q, rq);
- write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ spin_lock_irqsave(&srp->rq_entry_lck, iflags);
if (unlikely(srp->orphan)) {
if (sfp->keep_orphan)
- srp->sg_io_owned = 0;
+ srp->sync_invoc = false;
else
- done = 0;
+ rqq_state = SG_RQ_BUSY;
}
- srp->done = done;
- write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ srp->rq_state = rqq_state;
+ spin_unlock_irqrestore(&srp->rq_entry_lck, iflags);
- if (likely(done)) {
- /* Now wake up any sg_read() that is waiting for this
- * packet.
+ if (likely(rqq_state == SG_RQ_AWAIT_READ)) {
+ /*
+ * Now wake up any sg_read() or ioctl(SG_IORECEIVE) that is
+ * waiting for this packet.
*/
wake_up_interruptible(&sfp->read_wait);
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
kref_put(&sfp->f_ref, sg_remove_sfp);
- } else {
- INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext);
- schedule_work(&srp->ew.work);
+ } else { /* clean up orphaned request that aren't being kept */
+ INIT_WORK(&srp->ew_orph.work, sg_rq_end_io_usercontext);
+ schedule_work(&srp->ew_orph.work);
}
}
@@ -1565,8 +1673,8 @@ sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
}
k = error;
- SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp,
- "sg_alloc: dev=%d \n", k));
+ SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, "%s: dev=%d\n",
+ __func__, k));
sprintf(disk->disk_name, "sg%d", k);
disk->first_minor = k;
sdp->disk = disk;
@@ -1710,7 +1818,7 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf)
SG_LOG(3, sdp, "%s\n", __func__);
read_lock_irqsave(&sdp->sfd_lock, iflags);
- list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) {
+ list_for_each_entry(sfp, &sdp->sfds, sfd_entry) {
wake_up_interruptible_all(&sfp->read_wait);
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
}
@@ -1796,22 +1904,31 @@ exit_sg(void)
static int
sg_start_req(struct sg_request *srp, u8 *cmd)
{
- int res;
struct request *rq;
- struct scsi_request *req;
+ struct scsi_request *scsi_rp;
struct sg_fd *sfp = srp->parentfp;
+ struct sg_device *sdp;
struct sg_io_hdr *hp = &srp->header;
+ struct sg_scatter_hold *req_schp = &srp->data;
+ struct request_queue *q;
+ struct rq_map_data *md;
+ u8 *long_cmdp = NULL;
+ bool reserved;
+ int res;
int dxfer_len = (int)hp->dxfer_len;
int dxfer_dir = hp->dxfer_direction;
- unsigned int iov_count = hp->iovec_count;
- struct sg_scatter_hold *req_schp = &srp->data;
- struct sg_scatter_hold *rsv_schp = &sfp->reserve;
- struct request_queue *q = sfp->parentdp->device->request_queue;
- struct rq_map_data *md, map_data;
int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ;
- u8 *long_cmdp = NULL;
+ unsigned long iflags;
+ unsigned int iov_count = hp->iovec_count;
+ struct rq_map_data map_data;
- SG_LOG(4, sfp->parentdp, "%s: dxfer_len=%d\n", __func__, dxfer_len);
+ if (unlikely(!sfp)) {
+ WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__);
+ return -EBADF;
+ }
+ sdp = sfp->parentdp;
+ SG_LOG(4, sdp, "%s: dxfer_len=%d\n", __func__, dxfer_len);
+ q = sdp->device->request_queue;
if (hp->cmd_len > BLK_MAX_CDB) {
long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL);
@@ -1831,51 +1948,51 @@ sg_start_req(struct sg_request *srp, u8 *cmd)
* not expect an EWOULDBLOCK from this condition.
*/
rq = blk_get_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ?
- REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0);
+ REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0);
if (IS_ERR(rq)) {
kfree(long_cmdp);
return PTR_ERR(rq);
}
- req = scsi_req(rq);
+ spin_lock_irqsave(&srp->rq_entry_lck, iflags);
+ scsi_rp = scsi_req(rq);
if (hp->cmd_len > BLK_MAX_CDB)
- req->cmd = long_cmdp;
- memcpy(req->cmd, cmd, hp->cmd_len);
- req->cmd_len = hp->cmd_len;
+ scsi_rp->cmd = long_cmdp;
+ memcpy(scsi_rp->cmd, cmd, hp->cmd_len);
+ scsi_rp->cmd_len = hp->cmd_len;
srp->rq = rq;
rq->end_io_data = srp;
- req->retries = SG_DEFAULT_RETRIES;
+ scsi_rp->retries = SG_DEFAULT_RETRIES;
+ srp->rq_state = SG_RQ_INFLIGHT;
+ reserved = (sfp->reserve_srp == srp);
+ spin_unlock_irqrestore(&srp->rq_entry_lck, iflags);
if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
return 0;
- if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO &&
+ if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) &&
dxfer_dir != SG_DXFER_UNKNOWN && !iov_count &&
- !sfp->parentdp->device->host->unchecked_isa_dma &&
+ !sdp->device->host->unchecked_isa_dma &&
blk_rq_aligned(q, (unsigned long)hp->dxferp, dxfer_len))
- md = NULL;
+ md = NULL; /* direct IO activate */
else
md = &map_data;
if (md) {
mutex_lock(&sfp->f_mutex);
- if (dxfer_len <= rsv_schp->dlen &&
- !sfp->res_in_use) {
- sfp->res_in_use = true;
- sg_link_reserve(sfp, srp, dxfer_len);
- } else if (hp->flags & SG_FLAG_MMAP_IO) {
- res = -EBUSY; /* sfp->res_in_use == true */
- if (dxfer_len > rsv_schp->dlen)
- res = -ENOMEM;
- mutex_unlock(&sfp->f_mutex);
- return res;
- } else {
- res = sg_build_indirect(req_schp, sfp, dxfer_len);
- if (res) {
+ if (hp->flags & SG_FLAG_MMAP_IO) {
+ if (!reserved || dxfer_len > req_schp->dlen) {
+ res = reserved ? -ENOMEM : -EBUSY;
mutex_unlock(&sfp->f_mutex);
return res;
}
+ } else if (req_schp->dlen == 0) {
+ res = sg_mk_sgat_dlen(srp, sfp, dxfer_len);
+ if (res) {
+ mutex_unlock(&sfp->f_mutex);
+ return res; /* will be negated errno */
+ }
}
mutex_unlock(&sfp->f_mutex);
@@ -1918,70 +2035,70 @@ sg_start_req(struct sg_request *srp, u8 *cmd)
hp->info |= SG_INFO_DIRECT_IO;
}
}
+ SG_LOG(6, sdp, "%s: started, %siovec_count=%u\n", __func__,
+ (md ? "" : "direct_io, "), iov_count);
return res;
}
-static int
-sg_finish_rem_req(struct sg_request *srp)
+/* clean up mid-level + block layer objects associate with finished request */
+static void
+sg_finish_scsi_blk_rq(struct sg_request *srp)
{
int ret = 0;
-
struct sg_fd *sfp = srp->parentfp;
- struct sg_scatter_hold *req_schp = &srp->data;
- SG_LOG(4, sfp->parentdp, "%s: res_used=%d\n", __func__,
- (int)srp->res_used);
- if (srp->bio)
+ if (unlikely(!sfp))
+ pr_warn("sg: %s: sfp unexpectedly NULL", __func__);
+ else
+ SG_LOG(4, sfp->parentdp, "%s: srp=0x%p%s\n", __func__, srp,
+ (sfp->reserve_srp == srp) ? " reserve" : "");
+ if (srp->bio) {
ret = blk_rq_unmap_user(srp->bio);
-
+ srp->bio = NULL;
+ }
if (srp->rq) {
scsi_req_free_cmd(scsi_req(srp->rq));
blk_put_request(srp->rq);
+ srp->rq = NULL;
}
-
- if (srp->res_used)
- sg_unlink_reserve(sfp, srp);
- else
- sg_remove_scat(sfp, req_schp);
-
- return ret;
}
static int
sg_build_sgat(struct sg_scatter_hold *schp, const struct sg_fd *sfp,
int tablesize)
{
- int sg_bufflen = tablesize * sizeof(struct page *);
+ int sgat_arrlen = tablesize * sizeof(struct page *);
gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN;
- schp->pages = kzalloc(sg_bufflen, gfp_flags);
- if (!schp->pages)
+ schp->pages = kzalloc(sgat_arrlen, gfp_flags);
+ if (unlikely(!schp->pages))
return -ENOMEM;
return tablesize; /* number of scat_gath elements allocated */
}
+/* Returns 0 for good, otherwise negated errno value */
static int
-sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp,
- int buff_size)
+sg_mk_sgat_dlen(struct sg_request *srp, struct sg_fd *sfp, int dlen)
{
- int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems;
+ int i, k, rem_sz, num, mx_sc_elems, order, align_sz;
+ int blk_size = dlen;
+ int ret_sz = 0;
int sg_tablesize = sfp->parentdp->sg_tablesize;
- int blk_size = buff_size, order;
gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO;
struct sg_device *sdp = sfp->parentdp;
+ struct sg_scatter_hold *schp = &srp->data;
- if (blk_size < 0)
+ if (unlikely(blk_size < 0))
return -EFAULT;
- if (0 == blk_size)
- ++blk_size; /* don't know why */
+ if (unlikely(blk_size == 0))
+ ++blk_size; /* don't remember why */
/* round request up to next highest SG_SECTOR_SZ byte boundary */
- blk_size = ALIGN(blk_size, SG_SECTOR_SZ);
- SG_LOG(4, sfp->parentdp, "%s: buff_size=%d, blk_size=%d\n",
- __func__, buff_size, blk_size);
+ align_sz = ALIGN(blk_size, SG_SECTOR_SZ);
+ SG_LOG(4, sdp, "%s: dlen=%d, align_sz=%d\n", __func__, dlen, align_sz);
/* N.B. ret_sz carried into this block ... */
mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize);
- if (mx_sc_elems < 0)
+ if (unlikely(mx_sc_elems < 0))
return mx_sc_elems; /* most likely -ENOMEM */
num = scatter_elem_sz;
@@ -1993,22 +2110,22 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp,
scatter_elem_sz_prev = num;
}
- if (sdp->device->host->unchecked_isa_dma)
+ if (sdp && sdp->device->host->unchecked_isa_dma)
gfp_mask |= GFP_DMA;
order = get_order(num);
retry:
ret_sz = 1 << (PAGE_SHIFT + order);
- for (k = 0, rem_sz = blk_size; rem_sz > 0 && k < mx_sc_elems;
- k++, rem_sz -= ret_sz) {
+ for (k = 0, rem_sz = align_sz; rem_sz > 0 && k < mx_sc_elems;
+ ++k, rem_sz -= ret_sz) {
num = (rem_sz > scatter_elem_sz_prev) ?
scatter_elem_sz_prev : rem_sz;
schp->pages[k] = alloc_pages(gfp_mask, order);
if (!schp->pages[k])
- goto out;
+ goto err_out;
if (num == scatter_elem_sz_prev) {
if (unlikely(ret_sz > scatter_elem_sz_prev)) {
@@ -2017,20 +2134,20 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp,
}
}
- SG_LOG(5, sfp->parentdp, "%s: k=%d, num=%d, ret_sz=%d\n",
- __func__, k, num, ret_sz);
- } /* end of for loop */
+ SG_LOG(5, sdp, "%s: k=%d, num=%d, ret_sz=%d\n", __func__, k,
+ num, ret_sz);
+ } /* end of for loop */
schp->page_order = order;
schp->num_sgat = k;
- SG_LOG(5, sfp->parentdp, "%s: num_sgat=%d, rem_sz=%d\n", __func__, k,
- rem_sz);
-
- schp->dlen = blk_size;
- if (rem_sz > 0) /* must have failed */
+ SG_LOG(5, sdp, "%s: num_sgat=%d, rem_sz=%d\n", __func__, k, rem_sz);
+ if (unlikely(rem_sz > 0)) /* must have failed */
return -ENOMEM;
+ schp->dlen = align_sz;
+ if (sfp->tot_fd_thresh)
+ atomic_add(align_sz, &sfp->sum_fd_dlens);
return 0;
-out:
+err_out:
for (i = 0; i < k; i++)
__free_pages(schp->pages[i], order);
@@ -2040,138 +2157,160 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp,
return -ENOMEM;
}
+/* Remove the data (possibly a sgat list) held by srp, not srp itself */
static void
-sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp)
+sg_remove_sgat(struct sg_request *srp)
{
- SG_LOG(4, sfp->parentdp, "%s: num_sgat=%d\n", __func__,
- schp->num_sgat);
- if (schp->pages) {
- if (!schp->dio_in_use) {
- int k;
-
- for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) {
- SG_LOG(5, sfp->parentdp, "%s: k=%d, pg=0x%p\n",
- __func__, k, schp->pages[k]);
- __free_pages(schp->pages[k], schp->page_order);
- }
+ int k;
+ void *p;
+ struct sg_scatter_hold *schp = &srp->data;
+ struct sg_fd *sfp = srp->parentfp;
+ struct sg_device *sdp;
- kfree(schp->pages);
+ sdp = (sfp ? sfp->parentdp : NULL);
+ SG_LOG(4, sdp, "%s: num_sgat=%d%s\n", __func__, schp->num_sgat,
+ (srp->parentfp ? (srp == sfp->reserve_srp) : false) ?
+ " [reserve]" : "");
+ if (schp->pages && !schp->dio_in_use) {
+ for (k = 0; k < schp->num_sgat; ++k) {
+ p = schp->pages[k];
+ SG_LOG(5, sdp, "%s: pg[%d]=0x%p\n", __func__, k, p);
+ if (unlikely(!p))
+ continue;
+ __free_pages(p, schp->page_order);
}
+ SG_LOG(5, sdp, "%s: pgs=0x%p\n", __func__, schp->pages);
}
memset(schp, 0, sizeof(*schp));
}
+/*
+ * sg v1 and v2 interface: with a command yielding a data-in buffer, after
+ * it has arrived in kernel memory, this function copies it to the user
+ * space, appended to given struct sg_header object. Return 0 if okay, else
+ * a negated errno value.
+ */
static int
-sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_read_xfer)
+sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_xfer)
{
+ int k, num, res;
+ struct page *pgp;
struct sg_scatter_hold *schp = &srp->data;
- int k, num;
- SG_LOG(4, srp->parentfp->parentdp, "%s: num_read_xfer=%d\n", __func__,
- num_read_xfer);
- if ((!outp) || (num_read_xfer <= 0))
- return 0;
+ SG_LOG(4, srp->parentfp->parentdp, "%s: num_xfer=%d\n", __func__,
+ num_xfer);
+ if (unlikely(!outp || num_xfer <= 0))
+ return (num_xfer == 0 && outp) ? 0 : -EINVAL;
num = 1 << (PAGE_SHIFT + schp->page_order);
- for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) {
- if (num > num_read_xfer) {
- if (__copy_to_user(outp, page_address(schp->pages[k]),
- num_read_xfer))
- return -EFAULT;
+ for (k = 0, res = 0; k < schp->num_sgat; ++k) {
+ pgp = schp->pages[k];
+ if (unlikely(!pgp)) {
+ res = -ENXIO;
+ break;
+ }
+ if (num > num_xfer) {
+ if (__copy_to_user(outp, page_address(pgp), num_xfer))
+ res = -EFAULT;
break;
} else {
- if (__copy_to_user(outp, page_address(schp->pages[k]),
- num))
- return -EFAULT;
- num_read_xfer -= num;
- if (num_read_xfer <= 0)
+ if (__copy_to_user(outp, page_address(pgp), num)) {
+ res = -EFAULT;
+ break;
+ }
+ num_xfer -= num;
+ if (num_xfer <= 0)
break;
outp += num;
}
}
-
- return 0;
-}
-
-static void
-sg_build_reserve(struct sg_fd *sfp, int req_size)
-{
- struct sg_scatter_hold *schp = &sfp->reserve;
-
- SG_LOG(4, sfp->parentdp, "%s: req_size=%d\n", __func__, req_size);
- do {
- if (req_size < PAGE_SIZE)
- req_size = PAGE_SIZE;
- if (0 == sg_build_indirect(schp, sfp, req_size))
- return;
- else
- sg_remove_scat(sfp, schp);
- req_size >>= 1; /* divide by 2 */
- } while (req_size > (PAGE_SIZE / 2));
+ return res;
}
-static void
-sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size)
+static struct sg_request *
+sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id)
{
- struct sg_scatter_hold *req_schp = &srp->data;
- struct sg_scatter_hold *rsv_schp = &sfp->reserve;
- int k, num, rem;
-
- srp->res_used = true;
- SG_LOG(4, sfp->parentdp, "%s: size=%d\n", __func__, size);
- rem = size;
-
- num = 1 << (PAGE_SHIFT + rsv_schp->page_order);
- for (k = 0; k < rsv_schp->num_sgat; k++) {
- if (rem <= num) {
- req_schp->num_sgat = k + 1;
- req_schp->pages = rsv_schp->pages;
+ struct sg_request *srp;
+ unsigned long iflags;
- req_schp->dlen = size;
- req_schp->page_order = rsv_schp->page_order;
- break;
- } else
- rem -= num;
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ list_for_each_entry(srp, &sfp->rq_list, rq_entry) {
+ spin_lock(&srp->rq_entry_lck);
+ /* look for requests that are ready + not SG_IO owned */
+ if ((srp->rq_state == SG_RQ_AWAIT_READ) && !srp->sync_invoc &&
+ (pack_id == -1 || srp->header.pack_id == pack_id)) {
+ /* guard against other readers */
+ srp->rq_state = SG_RQ_DONE_READ;
+ spin_unlock(&srp->rq_entry_lck);
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return srp;
+ }
+ spin_unlock(&srp->rq_entry_lck);
}
-
- if (k >= rsv_schp->num_sgat)
- SG_LOG(1, sfp->parentdp, "%s: BAD size\n", __func__);
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return NULL;
}
-static void
-sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp)
+/* If rwlp and iflagsp non-NULL then release and re-take write lock */
+static struct sg_request *
+sg_mk_srp(struct sg_fd *sfp, bool first, rwlock_t *rwlp,
+ unsigned long *iflagsp)
{
- struct sg_scatter_hold *req_schp = &srp->data;
+ struct sg_request *srp;
+ int gfp = __GFP_NOWARN;
- SG_LOG(4, srp->parentfp->parentdp, "%s: req->num_sgat=%d\n", __func__,
- (int)req_schp->num_sgat);
- req_schp->num_sgat = 0;
- req_schp->dlen = 0;
- req_schp->pages = NULL;
- req_schp->page_order = 0;
- srp->res_used = false;
- /* Called without mutex lock to avoid deadlock */
- sfp->res_in_use = false;
+ if (first) { /* prepared to wait if none already outstanding */
+ if (rwlp && iflagsp) {
+ write_unlock_irqrestore(rwlp, *iflagsp);
+ srp = kzalloc(sizeof(*srp), gfp | GFP_KERNEL);
+ write_lock_irqsave(rwlp, *iflagsp);
+ } else
+ srp = kzalloc(sizeof(*srp), gfp | GFP_KERNEL);
+ } else
+ srp = kzalloc(sizeof(*srp), gfp | GFP_ATOMIC);
+ if (srp) {
+ spin_lock_init(&srp->rq_entry_lck);
+ srp->rq_state = SG_RQ_INACTIVE;
+ srp->parentfp = sfp;
+ return srp;
+ } else
+ return ERR_PTR(-ENOMEM);
}
+/*
+ * Irrespective of the given reserve buffer size, the minimum size requested
+ * will be PAGE_SIZE (often that is 4096 bytes). Returns a pointer to reserve
+ * object or a negated errno value twisted by ERR_PTR() macro. The actual
+ * number of bytes allocated (maybe less than dlen) is in srp->data.dlen .
+ * Note that this function is only called in contexts where locking is
+ * not required.
+ */
static struct sg_request *
-sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id)
+sg_build_reserve(struct sg_fd *sfp, int dlen)
{
- struct sg_request *resp;
- unsigned long iflags;
+ bool go_out = false;
+ int res;
+ struct sg_request *srp;
- write_lock_irqsave(&sfp->rq_list_lock, iflags);
- list_for_each_entry(resp, &sfp->rq_list, entry) {
- /* look for requests that are ready + not SG_IO owned */
- if ((1 == resp->done) && (!resp->sg_io_owned) &&
- ((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
- resp->done = 2; /* guard against other readers */
- write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return resp;
+ SG_LOG(4, sfp->parentdp, "%s: dlen=%d\n", __func__, dlen);
+ srp = sg_mk_srp(sfp, list_empty(&sfp->rq_free_list), NULL, NULL);
+ if (IS_ERR(srp))
+ return srp;
+ sfp->reserve_srp = srp;
+ do {
+ if (dlen < PAGE_SIZE) {
+ dlen = PAGE_SIZE;
+ go_out = true;
}
- }
- write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return NULL;
+ res = sg_mk_sgat_dlen(srp, sfp, dlen);
+ if (res == 0)
+ return srp;
+ if (go_out)
+ return ERR_PTR(res);
+ /* failed so remove, halve dlen, try again */
+ sg_remove_sgat(srp);
+ dlen >>= 1; /* divide by 2 */
+ } while (true);
}
/*
@@ -2181,33 +2320,87 @@ sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id)
* negated errno value twisted by ERR_PTR() macro.
*/
static struct sg_request *
-sg_add_request(struct sg_fd *sfp)
+sg_add_request(struct sg_fd *sfp, int dxfr_len, bool sync)
{
- int k;
+ bool done = false;
+ u32 sum_dlen;
unsigned long iflags;
- struct sg_request *rp = sfp->req_arr;
+ struct sg_request *srp = NULL;
+ struct sg_device *sdp;
+ const char *cp = "fail";
write_lock_irqsave(&sfp->rq_list_lock, iflags);
- if (!list_empty(&sfp->rq_list)) {
- if (!sfp->cmd_q)
- goto out_unlock;
-
- for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
- if (!rp->parentfp)
- break;
+ sdp = sfp->parentdp;
+ if (!list_empty(&sfp->rq_free_list)) {
+ /* when no data xfer, take last if not reserve request */
+ if (dxfr_len < 1) {
+ srp = list_last_entry(&sfp->rq_free_list,
+ struct sg_request, free_entry);
+ spin_lock(&srp->rq_entry_lck);
+ if (srp->rq_state == SG_RQ_INACTIVE &&
+ sfp->reserve_srp != srp) {
+ srp->rq_state = SG_RQ_BUSY;
+ cp = "re-using last in fl";
+ done = true;
+ } else
+ spin_unlock(&srp->rq_entry_lck);
+ } else { /* find request with large enough dlen */
+ list_for_each_entry(srp, &sfp->rq_free_list,
+ free_entry) {
+ spin_lock(&srp->rq_entry_lck);
+ if (srp->rq_state == SG_RQ_INACTIVE &&
+ srp->data.dlen >= dxfr_len) {
+ srp->rq_state = SG_RQ_BUSY;
+ cp = "re-using from start of fl";
+ done = true;
+ break;
+ }
+ spin_unlock(&srp->rq_entry_lck);
+ }
}
- if (k >= SG_MAX_QUEUE)
- goto out_unlock;
+ if (done) {
+ list_del(&srp->free_entry);
+ /* re-using request, may sure it's clean */
+ srp->orphan = false;
+ srp->v4_active = false;
+ srp->rq_state = SG_RQ_INACTIVE;
+ srp->d2p = NULL;
+ } else
+ srp = NULL;
}
- memset(rp, 0, sizeof(*rp));
- rp->parentfp = sfp;
- rp->header.duration = jiffies_to_msecs(jiffies);
- list_add_tail(&rp->entry, &sfp->rq_list);
- write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return rp;
-out_unlock:
+ if (!done) { /* Need new sg_request object */
+ bool empty = list_empty(&sfp->rq_list);
+
+ if (!sfp->cmd_q && !empty) {
+ srp = ERR_PTR(-EDOM);
+ SG_LOG(6, sdp, "%s: cmd_q false, trying second rq\n",
+ __func__);
+ goto out_wr_unlock;
+ }
+ if (sfp->tot_fd_thresh) {
+ sum_dlen = atomic_add_return(dxfr_len,
+ &sfp->sum_fd_dlens);
+ if (sum_dlen > sfp->tot_fd_thresh) {
+ srp = ERR_PTR(-E2BIG);
+ SG_LOG(2, sdp, "%s: sum_of_dlen(%u) > %s\n",
+ __func__, sum_dlen, "tot_fd_thresh");
+ atomic_sub(dxfr_len, &sfp->sum_fd_dlens);
+ goto out_wr_unlock;
+ }
+ }
+ srp = sg_mk_srp(sfp, empty, &sfp->rq_list_lock, &iflags);
+ if (IS_ERR(srp))
+ goto out_wr_unlock;
+ cp = "new";
+ }
+ srp->sync_invoc = sync;
+ if (done)
+ spin_unlock(&srp->rq_entry_lck);
+ list_add_tail(&srp->rq_entry, &sfp->rq_list);
+out_wr_unlock:
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return NULL;
+ SG_LOG(6, sdp, "%s: %s srp=0x%p\n", __func__, cp, srp);
+ return srp;
}
/*
@@ -2218,38 +2411,84 @@ sg_add_request(struct sg_fd *sfp)
* data length exceeds rem_sgat_thresh then the data (or sgat) is
* cleared and the request is appended to the tail of the free list.
*/
-static int
+static void
sg_remove_request(struct sg_fd *sfp, struct sg_request *srp)
{
+ bool reserve;
unsigned long iflags;
- int res = 0;
+ const char *cp = "head";
+ char b[64];
- if (!sfp || !srp || list_empty(&sfp->rq_list))
- return res;
+ if (WARN_ON(!sfp || !srp))
+ return;
write_lock_irqsave(&sfp->rq_list_lock, iflags);
- if (!list_empty(&srp->entry)) {
- list_del(&srp->entry);
- srp->parentfp = NULL;
- res = 1;
- }
+ spin_lock(&srp->rq_entry_lck);
+ /*
+ * N.B. sg_request object not de-allocated (freed). The contents of
+ * rq_list and rq_free_list lists are de-allocated (freed) when the
+ * owning file descriptor is closed. The free list acts as a LIFO.
+ * This can improve the chance of a cache hit when request is re-used.
+ */
+ reserve = (sfp->reserve_srp == srp);
+ if (reserve || srp->data.dlen <= sfp->rem_sgat_thresh) {
+ list_del(&srp->rq_entry);
+ if (srp->data.dlen > 0)
+ list_add(&srp->free_entry, &sfp->rq_free_list);
+ else {
+ list_add_tail(&srp->free_entry, &sfp->rq_free_list);
+ cp = "tail";
+ }
+ snprintf(b, sizeof(b), "%ssrp=0x%p move to fl %s",
+ (reserve ? "reserve " : ""), srp, cp);
+ } else {
+ srp->rq_state = SG_RQ_BUSY;
+ list_del(&srp->rq_entry);
+ spin_unlock(&srp->rq_entry_lck);
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ if (sfp->tot_fd_thresh) {
+ int dl = srp->data.dlen;
+
+ /* this is a subtraction, error if it goes negative */
+ if (atomic_add_negative(-dl, &sfp->sum_fd_dlens)) {
+ SG_LOG(2, sfp->parentdp,
+ "%s: logic error: this dlen > %s\n",
+ __func__, "sum_fd_dlens");
+ atomic_set(&sfp->sum_fd_dlens, 0);
+ }
+ }
+ sg_remove_sgat(srp);
+ /* don't kfree(srp), move clear request to tail of fl */
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ spin_lock(&srp->rq_entry_lck);
+ list_add_tail(&srp->free_entry, &sfp->rq_free_list);
+ snprintf(b, sizeof(b), "clear sgat srp=0x%p move to fl tail",
+ srp);
+ }
+ srp->rq_state = SG_RQ_INACTIVE;
+ spin_unlock(&srp->rq_entry_lck);
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return res;
+ SG_LOG(5, sfp->parentdp, "%s: %s\n", __func__, b);
}
static struct sg_fd *
sg_add_sfp(struct sg_device *sdp)
{
- struct sg_fd *sfp;
+ bool reduced = false;
+ int dlen;
unsigned long iflags;
- int bufflen;
+ long err;
+ struct sg_fd *sfp;
+ struct sg_request *srp;
sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);
- if (!sfp)
+ if (!sfp) {
+ SG_LOG(1, sdp, "%s: sfp allocation failed\n", __func__);
return ERR_PTR(-ENOMEM);
-
+ }
init_waitqueue_head(&sfp->read_wait);
rwlock_init(&sfp->rq_list_lock);
INIT_LIST_HEAD(&sfp->rq_list);
+ INIT_LIST_HEAD(&sfp->rq_free_list);
kref_init(&sfp->f_ref);
mutex_init(&sfp->f_mutex);
sfp->timeout = SG_DEFAULT_TIMEOUT;
@@ -2257,27 +2496,44 @@ sg_add_sfp(struct sg_device *sdp)
sfp->force_packid = !!SG_DEF_FORCE_PACK_ID;
sfp->cmd_q = !!SG_DEF_COMMAND_Q;
sfp->keep_orphan = !!SG_DEF_KEEP_ORPHAN;
+ sfp->rem_sgat_thresh = SG_RQ_DATA_THRESHOLD;
+ sfp->tot_fd_thresh = SG_TOT_FD_THRESHOLD;
+ atomic_set(&sfp->sum_fd_dlens, 0);
+ sfp->time_in_ns = !!SG_DEF_TIME_UNIT;
sfp->parentdp = sdp;
- write_lock_irqsave(&sdp->sfd_lock, iflags);
if (atomic_read(&sdp->detaching)) {
- write_unlock_irqrestore(&sdp->sfd_lock, iflags);
kfree(sfp);
+ SG_LOG(1, sdp, "%s: detaching\n", __func__);
return ERR_PTR(-ENODEV);
}
- list_add_tail(&sfp->sfd_siblings, &sdp->sfds);
- write_unlock_irqrestore(&sdp->sfd_lock, iflags);
- SG_LOG(3, sdp, "%s: sfp=0x%p\n", __func__, sfp);
if (unlikely(sg_big_buff != def_reserved_size))
sg_big_buff = def_reserved_size;
- bufflen = min_t(int, sg_big_buff,
+ dlen = min_t(int, sg_big_buff,
max_sectors_bytes(sdp->device->request_queue));
- sg_build_reserve(sfp, bufflen);
- SG_LOG(3, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__,
- sfp->reserve.dlen, sfp->reserve.num_sgat);
-
+ if (dlen > 0) {
+ srp = sg_build_reserve(sfp, dlen);
+ if (IS_ERR(srp)) {
+ kfree(sfp);
+ err = PTR_ERR(srp);
+ SG_LOG(1, sdp, "%s: build reserve err=%ld\n", __func__,
+ -err);
+ return ERR_PTR(err);
+ }
+ if (srp->data.dlen < dlen) {
+ reduced = true;
+ SG_LOG(2, sdp,
+ "%s: reserve reduced from %d to dlen=%d\n",
+ __func__, dlen, srp->data.dlen);
+ }
+ } else if (!reduced)
+ SG_LOG(4, sdp, "%s: built reserve dlen=%d\n", __func__, dlen);
+ write_lock_irqsave(&sdp->sfd_lock, iflags);
+ list_add_tail(&sfp->sfd_entry, &sdp->sfds);
kref_get(&sdp->d_ref);
__module_get(THIS_MODULE);
+ write_unlock_irqrestore(&sdp->sfd_lock, iflags);
+ SG_LOG(3, sdp, "%s: sfp=0x%p success\n", __func__, sfp);
return sfp;
}
@@ -2295,31 +2551,35 @@ sg_remove_sfp_usercontext(struct work_struct *work)
struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
struct sg_device *sdp = sfp->parentdp;
struct sg_request *srp;
- unsigned long iflags;
+ const char *cp = " srp=0x";
/* Cleanup any responses which were never read(). */
- write_lock_irqsave(&sfp->rq_list_lock, iflags);
while (!list_empty(&sfp->rq_list)) {
- srp = list_first_entry(&sfp->rq_list, struct sg_request,
- entry);
- sg_finish_rem_req(srp);
- list_del(&srp->entry);
- srp->parentfp = NULL;
+ srp = list_last_entry(&sfp->rq_list, struct sg_request,
+ rq_entry);
+ sg_finish_scsi_blk_rq(srp);
+ list_del(&srp->rq_entry);
+ if (srp->data.dlen > 0)
+ sg_remove_sgat(srp);
+ SG_LOG(6, sdp, "%s:%s%p\n", __func__, cp, srp);
+ kfree(srp);
+ }
+ while (!list_empty(&sfp->rq_free_list)) {
+ srp = list_last_entry(&sfp->rq_free_list, struct sg_request,
+ free_entry);
+ list_del(&srp->free_entry);
+ if (srp->data.dlen > 0)
+ sg_remove_sgat(srp);
+ SG_LOG(6, sdp, "%s: free list%s%p\n", __func__, cp, srp);
+ kfree(srp);
}
- write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-
- if (sfp->reserve.dlen > 0) {
- SG_LOG(6, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__,
- (int)sfp->reserve.dlen,
- (int)sfp->reserve.num_sgat);
- sg_remove_scat(sfp, &sfp->reserve);
- }
-
SG_LOG(6, sdp, "%s: sfp=0x%p\n", __func__, sfp);
kfree(sfp);
- scsi_device_put(sdp->device);
- kref_put(&sdp->d_ref, sg_device_destroy);
+ if (sdp) {
+ scsi_device_put(sdp->device);
+ kref_put(&sdp->d_ref, sg_device_destroy);
+ }
module_put(THIS_MODULE);
}
@@ -2331,7 +2591,7 @@ sg_remove_sfp(struct kref *kref)
unsigned long iflags;
write_lock_irqsave(&sdp->sfd_lock, iflags);
- list_del(&sfp->sfd_siblings);
+ list_del(&sfp->sfd_entry);
write_unlock_irqrestore(&sdp->sfd_lock, iflags);
INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext);
@@ -2375,13 +2635,13 @@ sg_lookup_dev(int dev)
* errno value on failure. Does not return NULL.
*/
static struct sg_device *
-sg_get_dev(int dev)
+sg_get_dev(int min_dev)
{
struct sg_device *sdp;
unsigned long flags;
read_lock_irqsave(&sg_index_lock, flags);
- sdp = sg_lookup_dev(dev);
+ sdp = sg_lookup_dev(min_dev);
if (!sdp)
sdp = ERR_PTR(-ENXIO);
else if (atomic_read(&sdp->detaching)) {
@@ -2475,6 +2735,7 @@ sg_proc_init(void)
return 0;
}
+
static int
sg_proc_seq_show_int(struct seq_file *s, void *v)
{
Remove fixed 16 sg_request object array and replace with an active rq_list plus a request free list. Add finer grained spin lock to sg_request and do a major rework on locking. sg_request objects now are only de-allocated when the owning file descriptor is closed. This simplifies locking issues. Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> --- This patch is big and complex. Towards the end the diff program completely loses the plot. Better to use difftool on a two pane window, or simply view the before sg.c and the after sg.c . drivers/scsi/sg.c | 1241 +++++++++++++++++++++++++++------------------ 1 file changed, 751 insertions(+), 490 deletions(-)