2019-05-21 00:08:10 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* 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.c):
|
|
|
|
* Copyright (C) 1992 Lawrence Foard
|
|
|
|
* Version 2 and 3 extensions to driver:
|
2014-06-04 00:18:18 +07:00
|
|
|
* Copyright (C) 1998 - 2014 Douglas Gilbert
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
2014-06-04 00:18:18 +07:00
|
|
|
static int sg_version_num = 30536; /* 2 digits for each component */
|
|
|
|
#define SG_VERSION_STR "3.5.36"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
2014-06-04 00:18:18 +07:00
|
|
|
* D. P. Gilbert (dgilbert@interlog.com), notes:
|
2005-04-17 05:20:36 +07:00
|
|
|
* - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
|
|
|
|
* the kernel/module needs to be built with CONFIG_SCSI_LOGGING
|
|
|
|
* (otherwise the macros compile to empty statements).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/mtio.h>
|
|
|
|
#include <linux/ioctl.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/cdev.h>
|
2007-08-06 01:36:11 +07:00
|
|
|
#include <linux/idr.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/delay.h>
|
2008-01-11 16:09:43 +07:00
|
|
|
#include <linux/blktrace_api.h>
|
2010-06-02 19:28:52 +07:00
|
|
|
#include <linux/mutex.h>
|
2014-06-25 19:08:03 +07:00
|
|
|
#include <linux/atomic.h>
|
2011-06-04 22:36:10 +07:00
|
|
|
#include <linux/ratelimit.h>
|
2015-02-22 23:58:50 +07:00
|
|
|
#include <linux/uio.h>
|
2018-06-25 21:25:44 +07:00
|
|
|
#include <linux/cred.h> /* for sg_check_file_access() */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include "scsi.h"
|
2005-04-04 02:53:59 +07:00
|
|
|
#include <scsi/scsi_dbg.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <scsi/scsi_host.h>
|
|
|
|
#include <scsi/scsi_driver.h>
|
|
|
|
#include <scsi/scsi_ioctl.h>
|
|
|
|
#include <scsi/sg.h>
|
|
|
|
|
|
|
|
#include "scsi_logging.h"
|
|
|
|
|
|
|
|
#ifdef CONFIG_SCSI_PROC_FS
|
|
|
|
#include <linux/proc_fs.h>
|
2014-06-04 00:18:18 +07:00
|
|
|
static char *sg_version_date = "20140603";
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int sg_proc_init(void);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SG_ALLOW_DIO_DEF 0
|
|
|
|
|
|
|
|
#define SG_MAX_DEVS 32768
|
|
|
|
|
2014-06-04 00:18:18 +07:00
|
|
|
/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type
|
|
|
|
* of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater
|
|
|
|
* than 16 bytes are "variable length" whose length is a multiple of 4
|
|
|
|
*/
|
|
|
|
#define SG_MAX_CDB_SIZE 252
|
|
|
|
|
2016-08-19 23:43:57 +07:00
|
|
|
#define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
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
|
|
|
|
of this size (or less if there is not enough memory) will be reserved
|
|
|
|
for use by this file descriptor. [Deprecated usage: this variable is also
|
|
|
|
readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into
|
|
|
|
the kernel (i.e. it is not a module).] */
|
|
|
|
static int def_reserved_size = -1; /* picks up init parameter */
|
|
|
|
static int sg_allow_dio = SG_ALLOW_DIO_DEF;
|
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
static int scatter_elem_sz = SG_SCATTER_SZ;
|
|
|
|
static int scatter_elem_sz_prev = SG_SCATTER_SZ;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define SG_SECTOR_SZ 512
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static int sg_add_device(struct device *, struct class_interface *);
|
|
|
|
static void sg_remove_device(struct device *, struct class_interface *);
|
2013-10-25 16:26:38 +07:00
|
|
|
|
2007-08-06 01:36:11 +07:00
|
|
|
static DEFINE_IDR(sg_index_idr);
|
2013-10-25 16:21:57 +07:00
|
|
|
static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock
|
|
|
|
file descriptor list for device */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static struct class_interface sg_interface = {
|
2014-06-25 19:08:03 +07:00
|
|
|
.add_dev = sg_add_device,
|
|
|
|
.remove_dev = sg_remove_device,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */
|
|
|
|
unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */
|
2007-08-05 22:31:24 +07:00
|
|
|
unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned bufflen; /* Size of (aggregate) data buffer */
|
2008-08-29 17:32:18 +07:00
|
|
|
struct page **pages;
|
|
|
|
int page_order;
|
2005-04-17 05:20:36 +07:00
|
|
|
char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */
|
|
|
|
unsigned char cmd_opcode; /* first byte of command */
|
|
|
|
} Sg_scatter_hold;
|
|
|
|
|
|
|
|
struct sg_device; /* forward declarations */
|
|
|
|
struct sg_fd;
|
|
|
|
|
|
|
|
typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
|
2017-04-07 14:34:16 +07:00
|
|
|
struct list_head entry; /* list entry */
|
2005-04-17 05:20:36 +07:00
|
|
|
struct sg_fd *parentfp; /* NULL -> not in use */
|
|
|
|
Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
|
|
|
|
sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */
|
2005-11-08 17:06:41 +07:00
|
|
|
unsigned char sense_b[SCSI_SENSE_BUFFERSIZE];
|
2005-04-17 05:20:36 +07:00
|
|
|
char res_used; /* 1 -> using reserve buffer, 0 -> not ... */
|
|
|
|
char orphan; /* 1 -> drop on sight, 0 -> normal */
|
|
|
|
char sg_io_owned; /* 1 -> packet belongs to SG_IO */
|
2012-04-13 04:33:58 +07:00
|
|
|
/* done protected by rq_list_lock */
|
|
|
|
char done; /* 0->before bh, 1->before read, 2->read */
|
2008-08-28 14:17:07 +07:00
|
|
|
struct request *rq;
|
2008-08-28 14:17:08 +07:00
|
|
|
struct bio *bio;
|
2009-02-04 09:36:27 +07:00
|
|
|
struct execute_work ew;
|
2005-04-17 05:20:36 +07:00
|
|
|
} Sg_request;
|
|
|
|
|
|
|
|
typedef struct sg_fd { /* holds the state of a file descriptor */
|
2014-06-25 19:08:03 +07:00
|
|
|
struct list_head sfd_siblings; /* protected by device's sfd_lock */
|
2005-04-17 05:20:36 +07:00
|
|
|
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 */
|
2017-04-07 14:34:14 +07:00
|
|
|
struct mutex f_mutex; /* protect against changes in this fd */
|
2005-04-17 05:20:36 +07:00
|
|
|
int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
|
|
|
|
int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
|
|
|
|
Sg_scatter_hold reserve; /* buffer held for this file descriptor */
|
2017-04-07 14:34:16 +07:00
|
|
|
struct list_head rq_list; /* head of request list */
|
2005-04-17 05:20:36 +07:00
|
|
|
struct fasync_struct *async_qp; /* used by asynchronous notification */
|
|
|
|
Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
|
|
|
|
char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */
|
|
|
|
char cmd_q; /* 1 -> allow command queuing, 0 -> don't */
|
2014-06-04 00:18:18 +07:00
|
|
|
unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */
|
2005-04-17 05:20:36 +07:00
|
|
|
char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */
|
|
|
|
char mmap_called; /* 0 -> mmap() never called on this fd */
|
2017-04-07 14:34:14 +07:00
|
|
|
char res_in_use; /* 1 -> 'reserve' array in use */
|
2009-01-22 02:45:50 +07:00
|
|
|
struct kref f_ref;
|
|
|
|
struct execute_work ew;
|
2005-04-17 05:20:36 +07:00
|
|
|
} Sg_fd;
|
|
|
|
|
|
|
|
typedef struct sg_device { /* holds the state of each scsi generic device */
|
|
|
|
struct scsi_device *device;
|
2014-06-25 19:08:03 +07:00
|
|
|
wait_queue_head_t open_wait; /* queue open() when O_EXCL present */
|
|
|
|
struct mutex open_rel_lock; /* held when in open() or release() */
|
2005-04-17 05:20:36 +07:00
|
|
|
int sg_tablesize; /* adapter's max scatter-gather table size */
|
2007-08-06 01:36:11 +07:00
|
|
|
u32 index; /* device index number */
|
2009-02-16 11:26:53 +07:00
|
|
|
struct list_head sfds;
|
2014-06-25 19:08:03 +07:00
|
|
|
rwlock_t sfd_lock; /* protect access to sfd 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) ) */
|
2005-04-17 05:20:36 +07:00
|
|
|
char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
|
|
|
|
struct gendisk *disk;
|
|
|
|
struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg<n>] */
|
2009-01-22 02:45:50 +07:00
|
|
|
struct kref d_ref;
|
2005-04-17 05:20:36 +07:00
|
|
|
} Sg_device;
|
|
|
|
|
2005-11-08 17:06:41 +07:00
|
|
|
/* tasklet or soft irq callback */
|
2017-06-03 14:38:04 +07:00
|
|
|
static void sg_rq_end_io(struct request *rq, blk_status_t status);
|
2008-08-28 14:17:07 +07:00
|
|
|
static int sg_start_req(Sg_request *srp, unsigned char *cmd);
|
2009-04-03 22:35:42 +07:00
|
|
|
static int sg_finish_rem_req(Sg_request * srp);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
|
|
|
|
static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
|
|
|
|
Sg_request * srp);
|
2008-06-26 18:48:27 +07:00
|
|
|
static ssize_t sg_new_write(Sg_fd *sfp, struct file *file,
|
|
|
|
const char __user *buf, size_t count, int blocking,
|
2009-01-21 05:00:09 +07:00
|
|
|
int read_only, int sg_io_owned, Sg_request **o_srp);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
|
|
|
|
unsigned char *cmnd, int timeout, int blocking);
|
|
|
|
static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer);
|
2014-06-25 21:39:55 +07:00
|
|
|
static void sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp);
|
2005-04-17 05:20:36 +07:00
|
|
|
static void sg_build_reserve(Sg_fd * sfp, int req_size);
|
|
|
|
static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);
|
|
|
|
static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);
|
2014-06-25 21:39:55 +07:00
|
|
|
static Sg_fd *sg_add_sfp(Sg_device * sdp);
|
2009-01-22 02:45:50 +07:00
|
|
|
static void sg_remove_sfp(struct kref *);
|
2005-04-17 05:20:36 +07:00
|
|
|
static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id);
|
|
|
|
static Sg_request *sg_add_request(Sg_fd * sfp);
|
|
|
|
static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
|
|
|
|
static Sg_device *sg_get_dev(int dev);
|
2014-06-25 19:08:03 +07:00
|
|
|
static void sg_device_destroy(struct kref *kref);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#define SZ_SG_HEADER sizeof(struct sg_header)
|
|
|
|
#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t)
|
|
|
|
#define SZ_SG_IOVEC sizeof(sg_iovec_t)
|
|
|
|
#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)
|
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
#define sg_printk(prefix, sdp, fmt, a...) \
|
2014-10-24 19:26:44 +07:00
|
|
|
sdev_prefix_printk(prefix, (sdp)->device, \
|
|
|
|
(sdp)->disk->disk_name, fmt, ##a)
|
2014-06-25 21:39:55 +07:00
|
|
|
|
2018-06-25 21:25:44 +07:00
|
|
|
/*
|
|
|
|
* The SCSI interfaces that use read() and write() as an asynchronous variant of
|
|
|
|
* ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways
|
|
|
|
* to trigger read() and write() calls from various contexts with elevated
|
|
|
|
* privileges. This can lead to kernel memory corruption (e.g. if these
|
|
|
|
* interfaces are called through splice()) and privilege escalation inside
|
|
|
|
* userspace (e.g. if a process with access to such a device passes a file
|
|
|
|
* descriptor to a SUID binary as stdin/stdout/stderr).
|
|
|
|
*
|
|
|
|
* This function provides protection for the legacy API by restricting the
|
|
|
|
* calling context.
|
|
|
|
*/
|
|
|
|
static int sg_check_file_access(struct file *filp, const char *caller)
|
|
|
|
{
|
|
|
|
if (filp->f_cred != current_real_cred()) {
|
|
|
|
pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
|
|
|
|
caller, task_tgid_vnr(current), current->comm);
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
if (uaccess_kernel()) {
|
|
|
|
pr_err_once("%s: process %d (%s) called from kernel context, this is not allowed.\n",
|
|
|
|
caller, task_tgid_vnr(current), current->comm);
|
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-26 16:03:24 +07:00
|
|
|
static int sg_allow_access(struct file *filp, unsigned char *cmd)
|
|
|
|
{
|
2010-09-05 08:52:46 +07:00
|
|
|
struct sg_fd *sfp = filp->private_data;
|
2008-07-26 16:03:24 +07:00
|
|
|
|
|
|
|
if (sfp->parentdp->device->type == TYPE_SCANNER)
|
|
|
|
return 0;
|
|
|
|
|
2017-11-05 14:36:31 +07:00
|
|
|
return blk_verify_command(cmd, filp->f_mode);
|
2008-07-26 16:03:24 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static int
|
|
|
|
open_wait(Sg_device *sdp, int flags)
|
2013-10-25 16:26:38 +07:00
|
|
|
{
|
2014-06-25 19:08:03 +07:00
|
|
|
int retval = 0;
|
2013-10-25 16:26:38 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
if (flags & O_EXCL) {
|
|
|
|
while (sdp->open_cnt > 0) {
|
|
|
|
mutex_unlock(&sdp->open_rel_lock);
|
|
|
|
retval = wait_event_interruptible(sdp->open_wait,
|
|
|
|
(atomic_read(&sdp->detaching) ||
|
|
|
|
!sdp->open_cnt));
|
|
|
|
mutex_lock(&sdp->open_rel_lock);
|
|
|
|
|
|
|
|
if (retval) /* -ERESTARTSYS */
|
|
|
|
return retval;
|
|
|
|
if (atomic_read(&sdp->detaching))
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (sdp->exclude) {
|
|
|
|
mutex_unlock(&sdp->open_rel_lock);
|
|
|
|
retval = wait_event_interruptible(sdp->open_wait,
|
|
|
|
(atomic_read(&sdp->detaching) ||
|
|
|
|
!sdp->exclude));
|
|
|
|
mutex_lock(&sdp->open_rel_lock);
|
|
|
|
|
|
|
|
if (retval) /* -ERESTARTSYS */
|
|
|
|
return retval;
|
|
|
|
if (atomic_read(&sdp->detaching))
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
}
|
2012-04-25 22:17:29 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
return retval;
|
2012-04-25 22:17:29 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
/* Returns 0 on success, else a negated errno value */
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
|
|
|
sg_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
int dev = iminor(inode);
|
|
|
|
int flags = filp->f_flags;
|
2005-11-08 17:06:41 +07:00
|
|
|
struct request_queue *q;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
nonseekable_open(inode, filp);
|
2014-06-25 19:08:03 +07:00
|
|
|
if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE)))
|
|
|
|
return -EPERM; /* Can't lock it with read only access */
|
2005-04-17 05:20:36 +07:00
|
|
|
sdp = sg_get_dev(dev);
|
2014-06-25 19:08:03 +07:00
|
|
|
if (IS_ERR(sdp))
|
|
|
|
return PTR_ERR(sdp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_open: flags=0x%x\n", flags));
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* This driver's module count bumped by fops_get in <linux/fs.h> */
|
|
|
|
/* Prevent the device driver from vanishing while we sleep */
|
|
|
|
retval = scsi_device_get(sdp->device);
|
2009-01-22 02:45:50 +07:00
|
|
|
if (retval)
|
|
|
|
goto sg_put;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
[SCSI] implement runtime Power Management
This patch (as1398b) adds runtime PM support to the SCSI layer. Only
the machanism is provided; use of it is up to the various high-level
drivers, and the patch doesn't change any of them. Except for sg --
the patch expicitly prevents a device from being runtime-suspended
while its sg device file is open.
The implementation is simplistic. In general, hosts and targets are
automatically suspended when all their children are asleep, but for
them the runtime-suspend code doesn't actually do anything. (A host's
runtime PM status is propagated up the device tree, though, so a
runtime-PM-aware lower-level driver could power down the host adapter
hardware at the appropriate times.) There are comments indicating
where a transport class might be notified or some other hooks added.
LUNs are runtime-suspended by calling the drivers' existing suspend
handlers (and likewise for runtime-resume). Somewhat arbitrarily, the
implementation delays for 100 ms before suspending an eligible LUN.
This is because there typically are occasions during bootup when the
same device file is opened and closed several times in quick
succession.
The way this all works is that the SCSI core increments a device's
PM-usage count when it is registered. If a high-level driver does
nothing then the device will not be eligible for runtime-suspend
because of the elevated usage count. If a high-level driver wants to
use runtime PM then it can call scsi_autopm_put_device() in its probe
routine to decrement the usage count and scsi_autopm_get_device() in
its remove routine to restore the original count.
Hosts, targets, and LUNs are not suspended while they are being probed
or removed, or while the error handler is running. In fact, a fairly
large part of the patch consists of code to make sure that things
aren't suspended at such times.
[jejb: fix up compile issues in PM config variations]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
2010-06-17 21:41:42 +07:00
|
|
|
retval = scsi_autopm_get_device(sdp->device);
|
|
|
|
if (retval)
|
|
|
|
goto sdp_put;
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
/* scsi_block_when_processing_errors() may block so bypass
|
|
|
|
* check if O_NONBLOCK. Permits SCSI commands to be issued
|
|
|
|
* during error recovery. Tread carefully. */
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!((flags & O_NONBLOCK) ||
|
|
|
|
scsi_block_when_processing_errors(sdp->device))) {
|
|
|
|
retval = -ENXIO;
|
|
|
|
/* we are in error recovery for this device */
|
|
|
|
goto error_out;
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
mutex_lock(&sdp->open_rel_lock);
|
|
|
|
if (flags & O_NONBLOCK) {
|
|
|
|
if (flags & O_EXCL) {
|
|
|
|
if (sdp->open_cnt > 0) {
|
|
|
|
retval = -EBUSY;
|
|
|
|
goto error_mutex_locked;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sdp->exclude) {
|
|
|
|
retval = -EBUSY;
|
|
|
|
goto error_mutex_locked;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
} else {
|
|
|
|
retval = open_wait(sdp, flags);
|
|
|
|
if (retval) /* -ERESTARTSYS or -ENODEV */
|
|
|
|
goto error_mutex_locked;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
|
|
|
|
/* N.B. at this point we are holding the open_rel_lock */
|
|
|
|
if (flags & O_EXCL)
|
|
|
|
sdp->exclude = true;
|
|
|
|
|
|
|
|
if (sdp->open_cnt < 1) { /* no existing opens */
|
2005-04-17 05:20:36 +07:00
|
|
|
sdp->sgdebug = 0;
|
2005-11-08 17:06:41 +07:00
|
|
|
q = sdp->device->request_queue;
|
2010-02-26 12:20:39 +07:00
|
|
|
sdp->sg_tablesize = queue_max_segments(q);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-06-25 21:39:55 +07:00
|
|
|
sfp = sg_add_sfp(sdp);
|
2014-06-25 19:08:03 +07:00
|
|
|
if (IS_ERR(sfp)) {
|
|
|
|
retval = PTR_ERR(sfp);
|
|
|
|
goto out_undo;
|
2013-10-25 16:27:02 +07:00
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
|
|
|
|
filp->private_data = sfp;
|
|
|
|
sdp->open_cnt++;
|
|
|
|
mutex_unlock(&sdp->open_rel_lock);
|
|
|
|
|
2013-10-25 16:27:02 +07:00
|
|
|
retval = 0;
|
2009-01-22 02:45:50 +07:00
|
|
|
sg_put:
|
2014-06-25 19:08:03 +07:00
|
|
|
kref_put(&sdp->d_ref, sg_device_destroy);
|
2005-04-17 05:20:36 +07:00
|
|
|
return retval;
|
2014-06-25 19:08:03 +07:00
|
|
|
|
|
|
|
out_undo:
|
|
|
|
if (flags & O_EXCL) {
|
|
|
|
sdp->exclude = false; /* undo if error */
|
|
|
|
wake_up_interruptible(&sdp->open_wait);
|
|
|
|
}
|
|
|
|
error_mutex_locked:
|
|
|
|
mutex_unlock(&sdp->open_rel_lock);
|
|
|
|
error_out:
|
|
|
|
scsi_autopm_put_device(sdp->device);
|
|
|
|
sdp_put:
|
|
|
|
scsi_device_put(sdp->device);
|
|
|
|
goto sg_put;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
/* Release resources associated with a successful sg_open()
|
|
|
|
* Returns 0 on success, else a negated errno value */
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
|
|
|
sg_release(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
|
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n"));
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
mutex_lock(&sdp->open_rel_lock);
|
[SCSI] implement runtime Power Management
This patch (as1398b) adds runtime PM support to the SCSI layer. Only
the machanism is provided; use of it is up to the various high-level
drivers, and the patch doesn't change any of them. Except for sg --
the patch expicitly prevents a device from being runtime-suspended
while its sg device file is open.
The implementation is simplistic. In general, hosts and targets are
automatically suspended when all their children are asleep, but for
them the runtime-suspend code doesn't actually do anything. (A host's
runtime PM status is propagated up the device tree, though, so a
runtime-PM-aware lower-level driver could power down the host adapter
hardware at the appropriate times.) There are comments indicating
where a transport class might be notified or some other hooks added.
LUNs are runtime-suspended by calling the drivers' existing suspend
handlers (and likewise for runtime-resume). Somewhat arbitrarily, the
implementation delays for 100 ms before suspending an eligible LUN.
This is because there typically are occasions during bootup when the
same device file is opened and closed several times in quick
succession.
The way this all works is that the SCSI core increments a device's
PM-usage count when it is registered. If a high-level driver does
nothing then the device will not be eligible for runtime-suspend
because of the elevated usage count. If a high-level driver wants to
use runtime PM then it can call scsi_autopm_put_device() in its probe
routine to decrement the usage count and scsi_autopm_get_device() in
its remove routine to restore the original count.
Hosts, targets, and LUNs are not suspended while they are being probed
or removed, or while the error handler is running. In fact, a fairly
large part of the patch consists of code to make sure that things
aren't suspended at such times.
[jejb: fix up compile issues in PM config variations]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
2010-06-17 21:41:42 +07:00
|
|
|
scsi_autopm_put_device(sdp->device);
|
2009-01-22 02:45:50 +07:00
|
|
|
kref_put(&sfp->f_ref, sg_remove_sfp);
|
2014-06-25 19:08:03 +07:00
|
|
|
sdp->open_cnt--;
|
|
|
|
|
|
|
|
/* possibly many open()s waiting on exlude clearing, start many;
|
|
|
|
* only open(O_EXCL)s wait on 0==open_cnt so only start one */
|
|
|
|
if (sdp->exclude) {
|
|
|
|
sdp->exclude = false;
|
|
|
|
wake_up_interruptible_all(&sdp->open_wait);
|
|
|
|
} else if (0 == sdp->open_cnt) {
|
|
|
|
wake_up_interruptible(&sdp->open_wait);
|
|
|
|
}
|
|
|
|
mutex_unlock(&sdp->open_rel_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
|
|
|
|
{
|
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
Sg_request *srp;
|
|
|
|
int req_pack_id = -1;
|
|
|
|
sg_io_hdr_t *hp;
|
2005-04-03 02:51:23 +07:00
|
|
|
struct sg_header *old_hdr = NULL;
|
|
|
|
int retval = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2018-06-25 21:25:44 +07:00
|
|
|
/*
|
|
|
|
* This could cause a response to be stranded. Close the associated
|
|
|
|
* file descriptor to free up any resources being held.
|
|
|
|
*/
|
|
|
|
retval = sg_check_file_access(filp, __func__);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_read: count=%d\n", (int) count));
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (sfp->force_packid && (count >= SZ_SG_HEADER)) {
|
2019-10-18 02:39:23 +07:00
|
|
|
old_hdr = memdup_user(buf, SZ_SG_HEADER);
|
|
|
|
if (IS_ERR(old_hdr))
|
|
|
|
return PTR_ERR(old_hdr);
|
2005-04-03 02:51:23 +07:00
|
|
|
if (old_hdr->reply_len < 0) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (count >= SZ_SG_IO_HDR) {
|
SCSI misc on 20191130
This is mostly update of the usual drivers: aacraid, ufs, zfcp,
NCR5380, lpfc, qla2xxx, smartpqi, hisi_sas, target, mpt3sas, pm80xx
plus a whole load of minor updates and fixes. The two major core
changes are Al Viro's reworking of sg's handling of copy to/from user,
Ming Lei's removal of the host busy counter to avoid contention in the
multiqueue case and Damien Le Moal's fixing of residual tracking
across error handling.
Signed-off-by: James E.J. Bottomley <jejb@linux.ibm.com>
-----BEGIN PGP SIGNATURE-----
iJwEABMIAEQWIQTnYEDbdso9F2cI+arnQslM7pishQUCXeKvHCYcamFtZXMuYm90
dG9tbGV5QGhhbnNlbnBhcnRuZXJzaGlwLmNvbQAKCRDnQslM7pishQJMAQDAjlAi
SNfbyndMqyf+rZGWufDI+43Up1VvW9GeWJHeDwEAxfO5XZsCks2uT8UxXhpEp9L7
HkiUww3zbcgl0FWFkUM=
=cdVU
-----END PGP SIGNATURE-----
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley:
"This is mostly update of the usual drivers: aacraid, ufs, zfcp,
NCR5380, lpfc, qla2xxx, smartpqi, hisi_sas, target, mpt3sas, pm80xx
plus a whole load of minor updates and fixes.
The major core changes are Al Viro's reworking of sg's handling of
copy to/from user, Ming Lei's removal of the host busy counter to
avoid contention in the multiqueue case and Damien Le Moal's fixing of
residual tracking across error handling"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (251 commits)
scsi: bnx2fc: timeout calculation invalid for bnx2fc_eh_abort()
scsi: target: core: Fix a pr_debug() argument
scsi: iscsi: Don't send data to unbound connection
scsi: target: iscsi: Wait for all commands to finish before freeing a session
scsi: target: core: Release SPC-2 reservations when closing a session
scsi: target: core: Document target_cmd_size_check()
scsi: bnx2i: fix potential use after free
Revert "scsi: qla2xxx: Fix memory leak when sending I/O fails"
scsi: NCR5380: Add disconnect_mask module parameter
scsi: NCR5380: Unconditionally clear ICR after do_abort()
scsi: NCR5380: Call scsi_set_resid() on command completion
scsi: scsi_debug: num_tgts must be >= 0
scsi: lpfc: use hdwq assigned cpu for allocation
scsi: arcmsr: fix indentation issues
scsi: qla4xxx: fix double free bug
scsi: pm80xx: Modified the logic to collect fatal dump
scsi: pm80xx: Tie the interrupt name to the module instance
scsi: pm80xx: Controller fatal error through sysfs
scsi: pm80xx: Do not request 12G sas speeds
scsi: pm80xx: Cleanup command when a reset times out
...
2019-12-03 04:37:02 +07:00
|
|
|
/*
|
|
|
|
* This is stupid.
|
|
|
|
*
|
|
|
|
* We're copying the whole sg_io_hdr_t from user
|
|
|
|
* space just to get the 'pack_id' field. But the
|
|
|
|
* field is at different offsets for the compat
|
|
|
|
* case, so we'll use "get_sg_io_hdr()" to copy
|
|
|
|
* the whole thing and convert it.
|
|
|
|
*
|
|
|
|
* We could do something like just calculating the
|
|
|
|
* offset based of 'in_compat_syscall()', but the
|
|
|
|
* 'compat_sg_io_hdr' definition is in the wrong
|
|
|
|
* place for that.
|
|
|
|
*/
|
2005-04-03 02:51:23 +07:00
|
|
|
sg_io_hdr_t *new_hdr;
|
|
|
|
new_hdr = kmalloc(SZ_SG_IO_HDR, GFP_KERNEL);
|
|
|
|
if (!new_hdr) {
|
|
|
|
retval = -ENOMEM;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2019-03-14 23:45:18 +07:00
|
|
|
retval = get_sg_io_hdr(new_hdr, buf);
|
2005-04-03 02:51:23 +07:00
|
|
|
req_pack_id = new_hdr->pack_id;
|
|
|
|
kfree(new_hdr);
|
|
|
|
if (retval) {
|
|
|
|
retval = -EFAULT;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
} else
|
2005-04-03 02:51:23 +07:00
|
|
|
req_pack_id = old_hdr->pack_id;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
srp = sg_get_rq_mark(sfp, req_pack_id);
|
|
|
|
if (!srp) { /* now wait on packet to arrive */
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching)) {
|
2005-04-03 02:51:23 +07:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
|
|
|
if (filp->f_flags & O_NONBLOCK) {
|
|
|
|
retval = -EAGAIN;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2012-04-13 04:33:25 +07:00
|
|
|
retval = wait_event_interruptible(sfp->read_wait,
|
2014-06-25 19:08:03 +07:00
|
|
|
(atomic_read(&sdp->detaching) ||
|
2012-04-13 04:33:25 +07:00
|
|
|
(srp = sg_get_rq_mark(sfp, req_pack_id))));
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching)) {
|
2012-04-13 04:32:48 +07:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
|
|
|
if (retval) {
|
2005-04-03 02:51:23 +07:00
|
|
|
/* -ERESTARTSYS as signal hit process */
|
|
|
|
goto free_old_hdr;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2005-04-03 02:51:23 +07:00
|
|
|
if (srp->header.interface_id != '\0') {
|
|
|
|
retval = sg_new_read(sfp, buf, count, srp);
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
hp = &srp->header;
|
2005-04-03 02:51:23 +07:00
|
|
|
if (old_hdr == NULL) {
|
|
|
|
old_hdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL);
|
|
|
|
if (! old_hdr) {
|
|
|
|
retval = -ENOMEM;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memset(old_hdr, 0, SZ_SG_HEADER);
|
|
|
|
old_hdr->reply_len = (int) hp->timeout;
|
|
|
|
old_hdr->pack_len = old_hdr->reply_len; /* old, strange behaviour */
|
|
|
|
old_hdr->pack_id = hp->pack_id;
|
|
|
|
old_hdr->twelve_byte =
|
2005-04-17 05:20:36 +07:00
|
|
|
((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0;
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->target_status = hp->masked_status;
|
|
|
|
old_hdr->host_status = hp->host_status;
|
|
|
|
old_hdr->driver_status = hp->driver_status;
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((CHECK_CONDITION & hp->masked_status) ||
|
|
|
|
(DRIVER_SENSE & hp->driver_status))
|
2005-04-03 02:51:23 +07:00
|
|
|
memcpy(old_hdr->sense_buffer, srp->sense_b,
|
|
|
|
sizeof (old_hdr->sense_buffer));
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (hp->host_status) {
|
|
|
|
/* This setup of 'result' is for backward compatibility and is best
|
|
|
|
ignored by the user who should use target, host + driver status */
|
|
|
|
case DID_OK:
|
|
|
|
case DID_PASSTHROUGH:
|
|
|
|
case DID_SOFT_ERROR:
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->result = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case DID_NO_CONNECT:
|
|
|
|
case DID_BUS_BUSY:
|
|
|
|
case DID_TIME_OUT:
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->result = EBUSY;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case DID_BAD_TARGET:
|
|
|
|
case DID_ABORT:
|
|
|
|
case DID_PARITY:
|
|
|
|
case DID_RESET:
|
|
|
|
case DID_BAD_INTR:
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->result = EIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case DID_ERROR:
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->result = (srp->sense_b[0] == 0 &&
|
2005-04-17 05:20:36 +07:00
|
|
|
hp->masked_status == GOOD) ? 0 : EIO;
|
|
|
|
break;
|
|
|
|
default:
|
2005-04-03 02:51:23 +07:00
|
|
|
old_hdr->result = EIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now copy the result back to the user buffer. */
|
|
|
|
if (count >= SZ_SG_HEADER) {
|
2019-10-18 02:39:23 +07:00
|
|
|
if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) {
|
2005-04-03 02:51:23 +07:00
|
|
|
retval = -EFAULT;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
buf += SZ_SG_HEADER;
|
2005-04-03 02:51:23 +07:00
|
|
|
if (count > old_hdr->reply_len)
|
|
|
|
count = old_hdr->reply_len;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (count > SZ_SG_HEADER) {
|
2005-04-03 02:51:23 +07:00
|
|
|
if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) {
|
|
|
|
retval = -EFAULT;
|
|
|
|
goto free_old_hdr;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
} else
|
2005-04-03 02:51:23 +07:00
|
|
|
count = (old_hdr->result == 0) ? 0 : -EIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
2005-04-03 02:51:23 +07:00
|
|
|
retval = count;
|
|
|
|
free_old_hdr:
|
2005-11-07 16:01:26 +07:00
|
|
|
kfree(old_hdr);
|
2005-04-03 02:51:23 +07:00
|
|
|
return retval;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp)
|
|
|
|
{
|
|
|
|
sg_io_hdr_t *hp = &srp->header;
|
2015-02-11 23:32:06 +07:00
|
|
|
int err = 0, err2;
|
2005-04-17 05:20:36 +07:00
|
|
|
int len;
|
|
|
|
|
|
|
|
if (count < SZ_SG_IO_HDR) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
hp->sb_len_wr = 0;
|
|
|
|
if ((hp->mx_sb_len > 0) && hp->sbp) {
|
|
|
|
if ((CHECK_CONDITION & hp->masked_status) ||
|
|
|
|
(DRIVER_SENSE & hp->driver_status)) {
|
2005-11-08 17:06:41 +07:00
|
|
|
int sb_len = SCSI_SENSE_BUFFERSIZE;
|
2005-04-17 05:20:36 +07:00
|
|
|
sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;
|
|
|
|
len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */
|
|
|
|
len = (len > sb_len) ? sb_len : len;
|
|
|
|
if (copy_to_user(hp->sbp, srp->sense_b, len)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
hp->sb_len_wr = len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hp->masked_status || hp->host_status || hp->driver_status)
|
|
|
|
hp->info |= SG_INFO_CHECK;
|
2019-03-14 23:45:18 +07:00
|
|
|
err = put_sg_io_hdr(hp, buf);
|
2008-09-02 20:50:07 +07:00
|
|
|
err_out:
|
2015-02-11 23:32:06 +07:00
|
|
|
err2 = sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
2015-02-11 23:32:06 +07:00
|
|
|
return err ? : err2 ? : count;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
unsigned char opcode;
|
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
Sg_request *srp;
|
|
|
|
struct sg_header old_hdr;
|
|
|
|
sg_io_hdr_t *hp;
|
2014-06-04 00:18:18 +07:00
|
|
|
unsigned char cmnd[SG_MAX_CDB_SIZE];
|
2018-06-25 21:25:44 +07:00
|
|
|
int retval;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2018-06-25 21:25:44 +07:00
|
|
|
retval = sg_check_file_access(filp, __func__);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2016-12-17 01:42:06 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_write: count=%d\n", (int) count));
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
|
|
|
if (!((filp->f_flags & O_NONBLOCK) ||
|
|
|
|
scsi_block_when_processing_errors(sdp->device)))
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
if (count < SZ_SG_HEADER)
|
|
|
|
return -EIO;
|
2019-10-18 02:39:24 +07:00
|
|
|
if (copy_from_user(&old_hdr, buf, SZ_SG_HEADER))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
blocking = !(filp->f_flags & O_NONBLOCK);
|
|
|
|
if (old_hdr.reply_len < 0)
|
2009-01-21 05:00:09 +07:00
|
|
|
return sg_new_write(sfp, filp, buf, count,
|
|
|
|
blocking, 0, 0, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (count < (SZ_SG_HEADER + 6))
|
|
|
|
return -EIO; /* The minimum scsi command length is 6 bytes. */
|
|
|
|
|
2019-10-18 02:39:20 +07:00
|
|
|
buf += SZ_SG_HEADER;
|
2019-10-18 02:39:24 +07:00
|
|
|
if (get_user(opcode, buf))
|
2019-10-18 02:39:20 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!(srp = sg_add_request(sfp))) {
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_write: queue full\n"));
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EDOM;
|
|
|
|
}
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_lock(&sfp->f_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (sfp->next_cmd_len > 0) {
|
|
|
|
cmd_size = sfp->next_cmd_len;
|
|
|
|
sfp->next_cmd_len = 0; /* reset so only this write() effected */
|
|
|
|
} else {
|
|
|
|
cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */
|
|
|
|
if ((opcode >= 0xc0) && old_hdr.twelve_byte)
|
|
|
|
cmd_size = 12;
|
|
|
|
}
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp,
|
2005-04-17 05:20:36 +07:00
|
|
|
"sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size));
|
|
|
|
/* Determine buffer size. */
|
|
|
|
input_size = count - cmd_size;
|
|
|
|
mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len;
|
|
|
|
mxsize -= SZ_SG_HEADER;
|
|
|
|
input_size -= SZ_SG_HEADER;
|
|
|
|
if (input_size < 0) {
|
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -EIO; /* User did not pass enough bytes for this command. */
|
|
|
|
}
|
|
|
|
hp = &srp->header;
|
|
|
|
hp->interface_id = '\0'; /* indicator of old interface tunnelled */
|
|
|
|
hp->cmd_len = (unsigned char) cmd_size;
|
|
|
|
hp->iovec_count = 0;
|
|
|
|
hp->mx_sb_len = 0;
|
|
|
|
if (input_size > 0)
|
|
|
|
hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ?
|
|
|
|
SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;
|
|
|
|
else
|
|
|
|
hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;
|
|
|
|
hp->dxfer_len = mxsize;
|
2016-03-03 12:31:29 +07:00
|
|
|
if ((hp->dxfer_direction == SG_DXFER_TO_DEV) ||
|
|
|
|
(hp->dxfer_direction == SG_DXFER_TO_FROM_DEV))
|
2008-09-02 14:20:20 +07:00
|
|
|
hp->dxferp = (char __user *)buf + cmd_size;
|
|
|
|
else
|
|
|
|
hp->dxferp = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
hp->sbp = NULL;
|
|
|
|
hp->timeout = old_hdr.reply_len; /* structure abuse ... */
|
|
|
|
hp->flags = input_size; /* structure abuse ... */
|
|
|
|
hp->pack_id = old_hdr.pack_id;
|
|
|
|
hp->usr_ptr = NULL;
|
2019-10-18 02:39:24 +07:00
|
|
|
if (copy_from_user(cmnd, buf, cmd_size))
|
2005-04-17 05:20:36 +07:00
|
|
|
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.
|
|
|
|
*/
|
2008-01-13 23:41:43 +07:00
|
|
|
if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) {
|
2017-04-07 14:34:15 +07:00
|
|
|
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",
|
|
|
|
old_hdr.reply_len - (int)SZ_SG_HEADER,
|
|
|
|
input_size, (unsigned int) cmnd[0],
|
|
|
|
current->comm);
|
2008-01-13 23:41:43 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
|
|
|
|
return (k < 0) ? k : count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
2008-06-26 18:48:27 +07:00
|
|
|
sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf,
|
2009-01-21 05:00:09 +07:00
|
|
|
size_t count, int blocking, int read_only, int sg_io_owned,
|
2008-06-26 18:48:27 +07:00
|
|
|
Sg_request **o_srp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int k;
|
|
|
|
Sg_request *srp;
|
|
|
|
sg_io_hdr_t *hp;
|
2014-06-04 00:18:18 +07:00
|
|
|
unsigned char cmnd[SG_MAX_CDB_SIZE];
|
2005-04-17 05:20:36 +07:00
|
|
|
int timeout;
|
|
|
|
unsigned long ul_timeout;
|
|
|
|
|
|
|
|
if (count < SZ_SG_IO_HDR)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */
|
|
|
|
if (!(srp = sg_add_request(sfp))) {
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_new_write: queue full\n"));
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EDOM;
|
|
|
|
}
|
2009-01-21 05:00:09 +07:00
|
|
|
srp->sg_io_owned = sg_io_owned;
|
2005-04-17 05:20:36 +07:00
|
|
|
hp = &srp->header;
|
2019-03-14 23:45:18 +07:00
|
|
|
if (get_sg_io_hdr(hp, buf)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
if (hp->interface_id != 'S') {
|
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
if (hp->flags & SG_FLAG_MMAP_IO) {
|
|
|
|
if (hp->dxfer_len > sfp->reserve.bufflen) {
|
|
|
|
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) */
|
|
|
|
}
|
2017-04-07 14:34:14 +07:00
|
|
|
if (sfp->res_in_use) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -EBUSY; /* reserve buffer already being used */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ul_timeout = msecs_to_jiffies(srp->header.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);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
2019-10-18 02:39:19 +07:00
|
|
|
if (copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2008-07-26 16:03:24 +07:00
|
|
|
if (read_only && sg_allow_access(file, cmnd)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
k = sg_common_write(sfp, srp, cmnd, timeout, blocking);
|
|
|
|
if (k < 0)
|
|
|
|
return k;
|
|
|
|
if (o_srp)
|
|
|
|
*o_srp = srp;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_common_write(Sg_fd * sfp, Sg_request * srp,
|
|
|
|
unsigned char *cmnd, int timeout, int blocking)
|
|
|
|
{
|
2015-01-30 22:22:38 +07:00
|
|
|
int k, at_head;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_device *sdp = sfp->parentdp;
|
|
|
|
sg_io_hdr_t *hp = &srp->header;
|
|
|
|
|
|
|
|
srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */
|
|
|
|
hp->status = 0;
|
|
|
|
hp->masked_status = 0;
|
|
|
|
hp->msg_status = 0;
|
|
|
|
hp->info = 0;
|
|
|
|
hp->host_status = 0;
|
|
|
|
hp->driver_status = 0;
|
|
|
|
hp->resid = 0;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n",
|
|
|
|
(int) cmnd[0], (int) hp->cmd_len));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2017-07-27 14:11:26 +07:00
|
|
|
if (hp->dxfer_len >= SZ_256M)
|
2017-04-07 14:34:15 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-08-28 14:17:07 +07:00
|
|
|
k = sg_start_req(srp, cmnd);
|
|
|
|
if (k) {
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_common_write: start_req err=%d\n", k));
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
2005-04-17 05:20:36 +07:00
|
|
|
return k; /* probably out of space --> ENOMEM */
|
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching)) {
|
2015-10-31 06:57:00 +07:00
|
|
|
if (srp->bio) {
|
2017-01-27 15:46:29 +07:00
|
|
|
scsi_req_free_cmd(scsi_req(srp->rq));
|
2018-10-16 21:38:47 +07:00
|
|
|
blk_put_request(srp->rq);
|
2015-10-31 06:57:00 +07:00
|
|
|
srp->rq = NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2005-04-03 02:51:23 +07:00
|
|
|
hp->duration = jiffies_to_msecs(jiffies);
|
2014-06-04 21:58:30 +07:00
|
|
|
if (hp->interface_id != '\0' && /* v3 (or later) interface */
|
|
|
|
(SG_FLAG_Q_AT_TAIL & hp->flags))
|
|
|
|
at_head = 0;
|
|
|
|
else
|
|
|
|
at_head = 1;
|
2008-08-29 17:32:18 +07:00
|
|
|
|
|
|
|
srp->rq->timeout = timeout;
|
2009-01-22 02:45:50 +07:00
|
|
|
kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */
|
2008-08-29 17:32:18 +07:00
|
|
|
blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk,
|
2014-06-04 21:58:30 +07:00
|
|
|
srp->rq, at_head, sg_rq_end_io);
|
2008-08-29 17:32:18 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-04-13 04:33:58 +07:00
|
|
|
static int srp_done(Sg_fd *sfp, 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;
|
|
|
|
}
|
|
|
|
|
2014-06-02 20:56:46 +07:00
|
|
|
static int max_sectors_bytes(struct request_queue *q)
|
|
|
|
{
|
|
|
|
unsigned int max_sectors = queue_max_sectors(q);
|
|
|
|
|
|
|
|
max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9);
|
|
|
|
|
|
|
|
return max_sectors << 9;
|
|
|
|
}
|
|
|
|
|
2017-09-15 19:05:15 +07:00
|
|
|
static void
|
|
|
|
sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
|
|
|
|
{
|
|
|
|
Sg_request *srp;
|
|
|
|
int val;
|
|
|
|
unsigned int ms;
|
|
|
|
|
|
|
|
val = 0;
|
|
|
|
list_for_each_entry(srp, &sfp->rq_list, entry) {
|
2017-10-16 00:16:33 +07:00
|
|
|
if (val >= SG_MAX_QUEUE)
|
2017-09-15 19:05:15 +07:00
|
|
|
break;
|
|
|
|
rinfo[val].req_state = srp->done + 1;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
rinfo[val].orphan = srp->orphan;
|
|
|
|
rinfo[val].sg_io_owned = srp->sg_io_owned;
|
|
|
|
rinfo[val].pack_id = srp->header.pack_id;
|
|
|
|
rinfo[val].usr_ptr = srp->header.usr_ptr;
|
|
|
|
val++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-24 19:53:13 +07:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
struct compat_sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
|
|
|
|
char req_state;
|
|
|
|
char orphan;
|
|
|
|
char sg_io_owned;
|
|
|
|
char problem;
|
|
|
|
int pack_id;
|
|
|
|
compat_uptr_t usr_ptr;
|
|
|
|
unsigned int duration;
|
|
|
|
int unused;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int put_compat_request_table(struct compat_sg_req_info __user *o,
|
|
|
|
struct sg_req_info *rinfo)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < SG_MAX_QUEUE; i++) {
|
|
|
|
if (copy_to_user(o + i, rinfo + i, offsetof(sg_req_info_t, usr_ptr)) ||
|
|
|
|
put_user((uintptr_t)rinfo[i].usr_ptr, &o[i].usr_ptr) ||
|
|
|
|
put_user(rinfo[i].duration, &o[i].duration) ||
|
|
|
|
put_user(rinfo[i].unused, &o[i].unused))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-04-13 04:35:05 +07:00
|
|
|
static long
|
2010-04-27 05:24:01 +07:00
|
|
|
sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
void __user *p = (void __user *)arg;
|
|
|
|
int __user *ip = p;
|
2014-10-11 17:06:47 +07:00
|
|
|
int result, val, read_only;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
Sg_request *srp;
|
|
|
|
unsigned long iflags;
|
|
|
|
|
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
2008-08-16 12:10:05 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_ioctl: cmd=0x%x\n", (int) cmd_in));
|
2005-04-17 05:20:36 +07:00
|
|
|
read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
|
|
|
|
|
|
|
|
switch (cmd_in) {
|
|
|
|
case SG_IO:
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2012-04-13 04:32:17 +07:00
|
|
|
return -ENODEV;
|
|
|
|
if (!scsi_block_when_processing_errors(sdp->device))
|
|
|
|
return -ENXIO;
|
|
|
|
result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
|
|
|
|
1, read_only, 1, &srp);
|
|
|
|
if (result < 0)
|
|
|
|
return result;
|
2012-04-13 04:33:25 +07:00
|
|
|
result = wait_event_interruptible(sfp->read_wait,
|
2014-06-25 19:08:03 +07:00
|
|
|
(srp_done(sfp, srp) || atomic_read(&sdp->detaching)));
|
|
|
|
if (atomic_read(&sdp->detaching))
|
2012-04-13 04:32:48 +07:00
|
|
|
return -ENODEV;
|
|
|
|
write_lock_irq(&sfp->rq_list_lock);
|
|
|
|
if (srp->done) {
|
|
|
|
srp->done = 2;
|
2012-04-13 04:32:17 +07:00
|
|
|
write_unlock_irq(&sfp->rq_list_lock);
|
2012-04-13 04:32:48 +07:00
|
|
|
result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
|
|
|
|
return (result < 0) ? result : 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2012-04-13 04:32:48 +07:00
|
|
|
srp->orphan = 1;
|
|
|
|
write_unlock_irq(&sfp->rq_list_lock);
|
|
|
|
return result; /* -ERESTARTSYS because signal hit process */
|
2005-04-17 05:20:36 +07:00
|
|
|
case SG_SET_TIMEOUT:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
if (val < 0)
|
|
|
|
return -EIO;
|
2016-08-19 23:43:57 +07:00
|
|
|
if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ))
|
|
|
|
val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ),
|
scsi: sg: Avoid overflow when USER_HZ > HZ
Calculating the maximum timeout that a user can set via the
SG_SET_TIMEOUT ioctl involves multiplying INT_MAX by USER_HZ/HZ. If
USER_HZ is larger than HZ then this results in an overflow when
performed as a 32 bit integer calculation, resulting in compiler
warnings such as the following:
drivers/scsi/sg.c: In function 'sg_ioctl':
drivers/scsi/sg.c:91:67: warning: integer overflow in expression [-Woverflow]
#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL))
^
drivers/scsi/sg.c:887:14: note: in expansion of macro 'MULDIV'
if (val >= MULDIV (INT_MAX, USER_HZ, HZ))
^
drivers/scsi/sg.c:91:67: warning: integer overflow in expression [-Woverflow]
#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL))
^
drivers/scsi/sg.c:888:13: note: in expansion of macro 'MULDIV'
val = MULDIV (INT_MAX, USER_HZ, HZ);
^
Avoid this overflow by performing the (constant) arithmetic on 64 bit
integers, which ensures that overflow from multiplying the 32 bit values
cannot occur. When converting the result back to a 32 bit integer use
min_t to ensure that we don't simply truncate a value beyond INT_MAX to
a 32 bit integer, but instead use INT_MAX where the result was larger
than it. As the values are all compile time constant the 64 bit
arithmetic should have no runtime cost.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2016-08-19 23:43:56 +07:00
|
|
|
INT_MAX);
|
2005-04-17 05:20:36 +07:00
|
|
|
sfp->timeout_user = val;
|
2016-08-19 23:43:57 +07:00
|
|
|
sfp->timeout = mult_frac(val, HZ, USER_HZ);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */
|
|
|
|
/* strange ..., for backward compatibility */
|
|
|
|
return sfp->timeout_user;
|
|
|
|
case SG_SET_FORCE_LOW_DMA:
|
2017-04-07 14:34:12 +07:00
|
|
|
/*
|
|
|
|
* N.B. This ioctl never worked properly, but failed to
|
|
|
|
* return an error value. So returning '0' to keep compability
|
|
|
|
* with legacy applications.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
case SG_GET_LOW_DMA:
|
2017-04-07 14:34:12 +07:00
|
|
|
return put_user((int) sdp->device->host->unchecked_isa_dma, ip);
|
2005-04-17 05:20:36 +07:00
|
|
|
case SG_GET_SCSI_ID:
|
2019-10-18 02:39:18 +07:00
|
|
|
{
|
|
|
|
sg_scsi_id_t v;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
2019-10-18 02:39:18 +07:00
|
|
|
memset(&v, 0, sizeof(v));
|
|
|
|
v.host_no = sdp->device->host->host_no;
|
|
|
|
v.channel = sdp->device->channel;
|
|
|
|
v.scsi_id = sdp->device->id;
|
|
|
|
v.lun = sdp->device->lun;
|
|
|
|
v.scsi_type = sdp->device->type;
|
|
|
|
v.h_cmd_per_lun = sdp->device->host->cmd_per_lun;
|
|
|
|
v.d_queue_depth = sdp->device->queue_depth;
|
|
|
|
if (copy_to_user(p, &v, sizeof(sg_scsi_id_t)))
|
|
|
|
return -EFAULT;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case SG_SET_FORCE_PACK_ID:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
sfp->force_packid = val ? 1 : 0;
|
|
|
|
return 0;
|
|
|
|
case SG_GET_PACK_ID:
|
|
|
|
read_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
list_for_each_entry(srp, &sfp->rq_list, entry) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((1 == srp->done) && (!srp->sg_io_owned)) {
|
|
|
|
read_unlock_irqrestore(&sfp->rq_list_lock,
|
|
|
|
iflags);
|
2019-10-18 02:39:18 +07:00
|
|
|
return put_user(srp->header.pack_id, ip);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
2019-10-18 02:39:18 +07:00
|
|
|
return put_user(-1, ip);
|
2005-04-17 05:20:36 +07:00
|
|
|
case SG_GET_NUM_WAITING:
|
|
|
|
read_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
val = 0;
|
|
|
|
list_for_each_entry(srp, &sfp->rq_list, entry) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((1 == srp->done) && (!srp->sg_io_owned))
|
|
|
|
++val;
|
|
|
|
}
|
|
|
|
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
return put_user(val, ip);
|
|
|
|
case SG_GET_SG_TABLESIZE:
|
|
|
|
return put_user(sdp->sg_tablesize, ip);
|
|
|
|
case SG_SET_RESERVED_SIZE:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
if (val < 0)
|
|
|
|
return -EINVAL;
|
2007-02-20 23:01:57 +07:00
|
|
|
val = min_t(int, val,
|
2014-06-02 20:56:46 +07:00
|
|
|
max_sectors_bytes(sdp->device->request_queue));
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_lock(&sfp->f_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (val != sfp->reserve.bufflen) {
|
2017-04-07 14:34:14 +07:00
|
|
|
if (sfp->mmap_called ||
|
|
|
|
sfp->res_in_use) {
|
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EBUSY;
|
2017-04-07 14:34:14 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_remove_scat(sfp, &sfp->reserve);
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_build_reserve(sfp, val);
|
|
|
|
}
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
case SG_GET_RESERVED_SIZE:
|
2007-02-20 23:01:57 +07:00
|
|
|
val = min_t(int, sfp->reserve.bufflen,
|
2014-06-02 20:56:46 +07:00
|
|
|
max_sectors_bytes(sdp->device->request_queue));
|
2005-04-17 05:20:36 +07:00
|
|
|
return put_user(val, ip);
|
|
|
|
case SG_SET_COMMAND_Q:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
sfp->cmd_q = val ? 1 : 0;
|
|
|
|
return 0;
|
|
|
|
case SG_GET_COMMAND_Q:
|
|
|
|
return put_user((int) sfp->cmd_q, ip);
|
|
|
|
case SG_SET_KEEP_ORPHAN:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
sfp->keep_orphan = val;
|
|
|
|
return 0;
|
|
|
|
case SG_GET_KEEP_ORPHAN:
|
|
|
|
return put_user((int) sfp->keep_orphan, ip);
|
|
|
|
case SG_NEXT_CMD_LEN:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
2017-02-16 05:11:54 +07:00
|
|
|
if (val > SG_MAX_CDB_SIZE)
|
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
sfp->next_cmd_len = (val > 0) ? val : 0;
|
|
|
|
return 0;
|
|
|
|
case SG_GET_VERSION_NUM:
|
|
|
|
return put_user(sg_version_num, ip);
|
|
|
|
case SG_GET_ACCESS_COUNT:
|
|
|
|
/* faked - we don't have a real access count anymore */
|
|
|
|
val = (sdp->device ? 1 : 0);
|
|
|
|
return put_user(val, ip);
|
|
|
|
case SG_GET_REQUEST_TABLE:
|
2018-08-24 19:53:13 +07:00
|
|
|
{
|
2005-04-03 02:51:23 +07:00
|
|
|
sg_req_info_t *rinfo;
|
|
|
|
|
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:03:40 +07:00
|
|
|
rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO,
|
2017-09-15 19:05:16 +07:00
|
|
|
GFP_KERNEL);
|
2005-04-03 02:51:23 +07:00
|
|
|
if (!rinfo)
|
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
read_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-09-15 19:05:15 +07:00
|
|
|
sg_fill_request_table(sfp, rinfo);
|
2005-04-17 05:20:36 +07:00
|
|
|
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
2018-08-24 19:53:13 +07:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
if (in_compat_syscall())
|
|
|
|
result = put_compat_request_table(p, rinfo);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
result = copy_to_user(p, rinfo,
|
|
|
|
SZ_SG_REQ_INFO * SG_MAX_QUEUE);
|
2005-04-03 02:51:23 +07:00
|
|
|
result = result ? -EFAULT : 0;
|
|
|
|
kfree(rinfo);
|
|
|
|
return result;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
case SG_EMULATED_HOST:
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
|
|
|
return put_user(sdp->device->host->hostt->emulated, ip);
|
|
|
|
case SCSI_IOCTL_SEND_COMMAND:
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
2008-09-03 04:16:41 +07:00
|
|
|
return sg_scsi_ioctl(sdp->device->request_queue, NULL, filp->f_mode, p);
|
2005-04-17 05:20:36 +07:00
|
|
|
case SG_SET_DEBUG:
|
|
|
|
result = get_user(val, ip);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
sdp->sgdebug = (char) val;
|
|
|
|
return 0;
|
2007-02-20 23:01:57 +07:00
|
|
|
case BLKSECTGET:
|
2014-06-02 20:56:46 +07:00
|
|
|
return put_user(max_sectors_bytes(sdp->device->request_queue),
|
2007-02-20 23:01:57 +07:00
|
|
|
ip);
|
2008-01-11 16:09:43 +07:00
|
|
|
case BLKTRACESETUP:
|
|
|
|
return blk_trace_setup(sdp->device->request_queue,
|
|
|
|
sdp->disk->disk_name,
|
2009-01-30 21:46:23 +07:00
|
|
|
MKDEV(SCSI_GENERIC_MAJOR, sdp->index),
|
2017-08-26 03:46:36 +07:00
|
|
|
NULL, p);
|
2008-01-11 16:09:43 +07:00
|
|
|
case BLKTRACESTART:
|
|
|
|
return blk_trace_startstop(sdp->device->request_queue, 1);
|
|
|
|
case BLKTRACESTOP:
|
|
|
|
return blk_trace_startstop(sdp->device->request_queue, 0);
|
|
|
|
case BLKTRACETEARDOWN:
|
|
|
|
return blk_trace_remove(sdp->device->request_queue);
|
2014-10-11 21:25:31 +07:00
|
|
|
case SCSI_IOCTL_GET_IDLUN:
|
|
|
|
case SCSI_IOCTL_GET_BUS_NUMBER:
|
|
|
|
case SCSI_IOCTL_PROBE_HOST:
|
|
|
|
case SG_GET_TRANSFORM:
|
|
|
|
case SG_SCSI_RESET:
|
|
|
|
if (atomic_read(&sdp->detaching))
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
|
|
|
if (read_only)
|
|
|
|
return -EPERM; /* don't know so take safe approach */
|
2014-10-11 21:25:31 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-10-11 21:25:31 +07:00
|
|
|
|
|
|
|
result = scsi_ioctl_block_when_processing_errors(sdp->device,
|
|
|
|
cmd_in, filp->f_flags & O_NDELAY);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
return scsi_ioctl(sdp->device, cmd_in, p);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
|
|
|
|
{
|
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
struct scsi_device *sdev;
|
|
|
|
|
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
sdev = sdp->device;
|
|
|
|
if (sdev->host->hostt->compat_ioctl) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-07-03 17:39:46 +07:00
|
|
|
static __poll_t
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_poll(struct file *filp, poll_table * wait)
|
|
|
|
{
|
2017-07-03 17:39:46 +07:00
|
|
|
__poll_t res = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
Sg_request *srp;
|
|
|
|
int count = 0;
|
|
|
|
unsigned long iflags;
|
|
|
|
|
2012-04-13 04:33:39 +07:00
|
|
|
sfp = filp->private_data;
|
|
|
|
if (!sfp)
|
2018-02-12 05:34:03 +07:00
|
|
|
return EPOLLERR;
|
2012-04-13 04:33:39 +07:00
|
|
|
sdp = sfp->parentdp;
|
|
|
|
if (!sdp)
|
2018-02-12 05:34:03 +07:00
|
|
|
return EPOLLERR;
|
2005-04-17 05:20:36 +07:00
|
|
|
poll_wait(filp, &sfp->read_wait, wait);
|
|
|
|
read_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
list_for_each_entry(srp, &sfp->rq_list, entry) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* if any read waiting, flag it */
|
|
|
|
if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))
|
2018-02-12 05:34:03 +07:00
|
|
|
res = EPOLLIN | EPOLLRDNORM;
|
2005-04-17 05:20:36 +07:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
2018-02-12 05:34:03 +07:00
|
|
|
res |= EPOLLHUP;
|
2005-04-17 05:20:36 +07:00
|
|
|
else if (!sfp->cmd_q) {
|
|
|
|
if (0 == count)
|
2018-02-12 05:34:03 +07:00
|
|
|
res |= EPOLLOUT | EPOLLWRNORM;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else if (count < SG_MAX_QUEUE)
|
2018-02-12 05:34:03 +07:00
|
|
|
res |= EPOLLOUT | EPOLLWRNORM;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
2017-07-17 10:54:30 +07:00
|
|
|
"sg_poll: res=0x%x\n", (__force u32) res));
|
2005-04-17 05:20:36 +07:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_fasync(int fd, struct file *filp, int mode)
|
|
|
|
{
|
|
|
|
Sg_device *sdp;
|
|
|
|
Sg_fd *sfp;
|
|
|
|
|
|
|
|
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
|
|
|
|
return -ENXIO;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_fasync: mode=%d\n", mode));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-02-02 04:52:56 +07:00
|
|
|
return fasync_helper(fd, filp, mode, &sfp->async_qp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2018-04-15 01:47:41 +07:00
|
|
|
static vm_fault_t
|
2017-02-25 05:56:41 +07:00
|
|
|
sg_vma_fault(struct vm_fault *vmf)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2017-02-25 05:56:41 +07:00
|
|
|
struct vm_area_struct *vma = vmf->vma;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_fd *sfp;
|
2005-11-08 17:06:41 +07:00
|
|
|
unsigned long offset, len, sa;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_scatter_hold *rsv_schp;
|
2008-08-29 17:32:18 +07:00
|
|
|
int k, length;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data)))
|
2008-02-08 09:46:06 +07:00
|
|
|
return VM_FAULT_SIGBUS;
|
2005-04-17 05:20:36 +07:00
|
|
|
rsv_schp = &sfp->reserve;
|
2008-02-08 09:46:06 +07:00
|
|
|
offset = vmf->pgoff << PAGE_SHIFT;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (offset >= rsv_schp->bufflen)
|
2008-02-08 09:46:06 +07:00
|
|
|
return VM_FAULT_SIGBUS;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_vma_fault: offset=%lu, scatg=%d\n",
|
|
|
|
offset, rsv_schp->k_use_sg));
|
2005-11-08 17:06:41 +07:00
|
|
|
sa = vma->vm_start;
|
2008-08-29 17:32:18 +07:00
|
|
|
length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
|
|
|
|
for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) {
|
2005-11-08 17:06:41 +07:00
|
|
|
len = vma->vm_end - sa;
|
2008-08-29 17:32:18 +07:00
|
|
|
len = (len < length) ? len : length;
|
2005-11-08 17:06:41 +07:00
|
|
|
if (offset < len) {
|
2008-08-29 17:32:18 +07:00
|
|
|
struct page *page = nth_page(rsv_schp->pages[k],
|
|
|
|
offset >> PAGE_SHIFT);
|
2005-11-08 17:06:41 +07:00
|
|
|
get_page(page); /* increment page count */
|
2008-02-08 09:46:06 +07:00
|
|
|
vmf->page = page;
|
|
|
|
return 0; /* success */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
sa += len;
|
|
|
|
offset -= len;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2008-02-08 09:46:06 +07:00
|
|
|
return VM_FAULT_SIGBUS;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-09-28 01:29:37 +07:00
|
|
|
static const struct vm_operations_struct sg_mmap_vm_ops = {
|
2008-02-08 09:46:06 +07:00
|
|
|
.fault = sg_vma_fault,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
|
|
{
|
|
|
|
Sg_fd *sfp;
|
2005-11-08 17:06:41 +07:00
|
|
|
unsigned long req_sz, len, sa;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_scatter_hold *rsv_schp;
|
2008-08-29 17:32:18 +07:00
|
|
|
int k, length;
|
2017-08-16 12:41:08 +07:00
|
|
|
int ret = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data)))
|
|
|
|
return -ENXIO;
|
2005-04-03 02:51:23 +07:00
|
|
|
req_sz = vma->vm_end - vma->vm_start;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_mmap starting, vm_start=%p, len=%d\n",
|
|
|
|
(void *) vma->vm_start, (int) req_sz));
|
2005-04-17 05:20:36 +07:00
|
|
|
if (vma->vm_pgoff)
|
|
|
|
return -EINVAL; /* want no offset */
|
|
|
|
rsv_schp = &sfp->reserve;
|
2017-08-16 12:41:08 +07:00
|
|
|
mutex_lock(&sfp->f_mutex);
|
|
|
|
if (req_sz > rsv_schp->bufflen) {
|
|
|
|
ret = -ENOMEM; /* cannot map more than reserved buffer */
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-08 17:06:41 +07:00
|
|
|
sa = vma->vm_start;
|
2008-08-29 17:32:18 +07:00
|
|
|
length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
|
|
|
|
for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) {
|
2005-11-08 17:06:41 +07:00
|
|
|
len = vma->vm_end - sa;
|
2008-08-29 17:32:18 +07:00
|
|
|
len = (len < length) ? len : length;
|
2005-11-08 17:06:41 +07:00
|
|
|
sa += len;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2006-03-22 15:08:30 +07:00
|
|
|
sfp->mmap_called = 1;
|
drivers/scsi/sg.c: mark VMA as VM_IO to prevent migration
Reduced testcase:
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <numaif.h>
#define SIZE 0x2000
int main()
{
int fd;
void *p;
fd = open("/dev/sg0", O_RDWR);
p = mmap(NULL, SIZE, PROT_EXEC, MAP_PRIVATE | MAP_LOCKED, fd, 0);
mbind(p, SIZE, 0, NULL, 0, MPOL_MF_MOVE);
return 0;
}
We shouldn't try to migrate pages in sg VMA as we don't have a way to
update Sg_scatter_hold::pages accordingly from mm core.
Let's mark the VMA as VM_IO to indicate to mm core that the VMA is not
migratable.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Shiraz Hashim <shashim@codeaurora.org>
Cc: Hugh Dickins <hughd@google.com>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: syzkaller <syzkaller@googlegroups.com>
Cc: Kostya Serebryany <kcc@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-02-03 07:57:35 +07:00
|
|
|
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
|
2005-04-17 05:20:36 +07:00
|
|
|
vma->vm_private_data = sfp;
|
|
|
|
vma->vm_ops = &sg_mmap_vm_ops;
|
2017-08-16 12:41:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sfp->f_mutex);
|
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
|
|
|
sg_rq_end_io_usercontext(struct work_struct *work)
|
2009-02-04 09:36:27 +07:00
|
|
|
{
|
|
|
|
struct sg_request *srp = container_of(work, struct sg_request, ew.work);
|
|
|
|
struct sg_fd *sfp = srp->parentfp;
|
|
|
|
|
|
|
|
sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
sg_remove_request(sfp, srp);
|
2009-02-04 09:36:27 +07:00
|
|
|
kref_put(&sfp->f_ref, sg_remove_sfp);
|
|
|
|
}
|
|
|
|
|
2008-09-02 20:50:01 +07:00
|
|
|
/*
|
|
|
|
* This function is a "bottom half" handler that is called by the mid
|
|
|
|
* level when a command is completed (or has failed).
|
|
|
|
*/
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
2017-06-03 14:38:04 +07:00
|
|
|
sg_rq_end_io(struct request *rq, blk_status_t status)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-09-02 20:50:01 +07:00
|
|
|
struct sg_request *srp = rq->end_io_data;
|
2017-01-27 15:46:29 +07:00
|
|
|
struct scsi_request *req = scsi_req(rq);
|
2009-01-22 02:45:50 +07:00
|
|
|
Sg_device *sdp;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_fd *sfp;
|
|
|
|
unsigned long iflags;
|
2005-04-03 02:51:23 +07:00
|
|
|
unsigned int ms;
|
2008-09-02 20:50:01 +07:00
|
|
|
char *sense;
|
2009-01-22 02:45:50 +07:00
|
|
|
int result, resid, done = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
if (WARN_ON(srp->done != 0))
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sfp = srp->parentfp;
|
2009-01-22 02:45:50 +07:00
|
|
|
if (WARN_ON(sfp == NULL))
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2009-01-22 02:45:50 +07:00
|
|
|
|
|
|
|
sdp = sfp->parentdp;
|
2014-06-25 19:08:03 +07:00
|
|
|
if (unlikely(atomic_read(&sdp->detaching)))
|
|
|
|
pr_info("%s: device detaching\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2017-01-27 15:46:29 +07:00
|
|
|
sense = req->sense;
|
2017-04-20 21:03:01 +07:00
|
|
|
result = req->result;
|
2017-01-27 15:46:29 +07:00
|
|
|
resid = req->resid_len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_cmd_done: pack_id=%d, res=0x%x\n",
|
|
|
|
srp->header.pack_id, result));
|
2005-11-08 17:06:41 +07:00
|
|
|
srp->header.resid = resid;
|
2005-04-03 02:51:23 +07:00
|
|
|
ms = jiffies_to_msecs(jiffies);
|
|
|
|
srp->header.duration = (ms > srp->header.duration) ?
|
|
|
|
(ms - srp->header.duration) : 0;
|
2005-11-08 17:06:41 +07:00
|
|
|
if (0 != result) {
|
2005-04-17 05:20:36 +07:00
|
|
|
struct scsi_sense_hdr sshdr;
|
|
|
|
|
2005-11-08 17:06:41 +07:00
|
|
|
srp->header.status = 0xff & result;
|
|
|
|
srp->header.masked_status = status_byte(result);
|
|
|
|
srp->header.msg_status = msg_byte(result);
|
|
|
|
srp->header.host_status = host_byte(result);
|
|
|
|
srp->header.driver_status = driver_byte(result);
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((sdp->sgdebug > 0) &&
|
|
|
|
((CHECK_CONDITION == srp->header.masked_status) ||
|
|
|
|
(COMMAND_TERMINATED == srp->header.masked_status)))
|
2014-10-24 19:26:45 +07:00
|
|
|
__scsi_print_sense(sdp->device, __func__, sense,
|
2005-11-08 17:06:41 +07:00
|
|
|
SCSI_SENSE_BUFFERSIZE);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Following if statement is a patch supplied by Eric Youngdale */
|
2005-11-08 17:06:41 +07:00
|
|
|
if (driver_byte(result) != 0
|
|
|
|
&& scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)
|
2005-04-17 05:20:36 +07:00
|
|
|
&& !scsi_sense_is_deferred(&sshdr)
|
|
|
|
&& sshdr.sense_key == UNIT_ATTENTION
|
|
|
|
&& sdp->device->removable) {
|
|
|
|
/* Detected possible disc change. Set the bit - this */
|
|
|
|
/* may be used if there are filesystems using this device */
|
|
|
|
sdp->device->changed = 1;
|
|
|
|
}
|
|
|
|
}
|
2017-01-27 15:46:29 +07:00
|
|
|
|
|
|
|
if (req->sense_len)
|
|
|
|
memcpy(srp->sense_b, req->sense, SCSI_SENSE_BUFFERSIZE);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Rely on write phase to clean out srp status values, so no "else" */
|
|
|
|
|
2015-02-14 00:09:44 +07:00
|
|
|
/*
|
|
|
|
* Free the request as soon as it is complete so that its resources
|
|
|
|
* can be reused without waiting for userspace to read() the
|
|
|
|
* result. But keep the associated bio (if any) around until
|
|
|
|
* blk_rq_unmap_user() can be called from user context.
|
|
|
|
*/
|
|
|
|
srp->rq = NULL;
|
2017-01-27 15:46:29 +07:00
|
|
|
scsi_req_free_cmd(scsi_req(rq));
|
2018-10-25 02:52:28 +07:00
|
|
|
blk_put_request(rq);
|
2015-02-14 00:09:44 +07:00
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
write_lock_irqsave(&sfp->rq_list_lock, iflags);
|
|
|
|
if (unlikely(srp->orphan)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (sfp->keep_orphan)
|
|
|
|
srp->sg_io_owned = 0;
|
2009-01-22 02:45:50 +07:00
|
|
|
else
|
|
|
|
done = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-01-22 02:45:50 +07:00
|
|
|
srp->done = done;
|
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
|
|
|
|
if (likely(done)) {
|
|
|
|
/* Now wake up any sg_read() that is waiting for this
|
|
|
|
* packet.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
wake_up_interruptible(&sfp->read_wait);
|
2009-01-22 02:45:50 +07:00
|
|
|
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
|
2009-02-04 09:36:27 +07:00
|
|
|
kref_put(&sfp->f_ref, sg_remove_sfp);
|
2009-04-03 17:28:06 +07:00
|
|
|
} else {
|
|
|
|
INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext);
|
|
|
|
schedule_work(&srp->ew.work);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-10-02 05:43:56 +07:00
|
|
|
static const struct file_operations sg_fops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = sg_read,
|
|
|
|
.write = sg_write,
|
|
|
|
.poll = sg_poll,
|
2012-04-13 04:35:05 +07:00
|
|
|
.unlocked_ioctl = sg_ioctl,
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = sg_compat_ioctl,
|
|
|
|
#endif
|
|
|
|
.open = sg_open,
|
|
|
|
.mmap = sg_mmap,
|
|
|
|
.release = sg_release,
|
|
|
|
.fasync = sg_fasync,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 23:52:59 +07:00
|
|
|
.llseek = no_llseek,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2005-03-24 00:55:22 +07:00
|
|
|
static struct class *sg_sysfs_class;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int sg_sysfs_valid = 0;
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static Sg_device *
|
|
|
|
sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-08 17:06:41 +07:00
|
|
|
struct request_queue *q = scsidp->request_queue;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_device *sdp;
|
|
|
|
unsigned long iflags;
|
2007-08-06 01:36:11 +07:00
|
|
|
int error;
|
|
|
|
u32 k;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-16 22:31:18 +07:00
|
|
|
sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!sdp) {
|
2014-06-25 19:08:03 +07:00
|
|
|
sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device "
|
|
|
|
"failure\n", __func__);
|
2007-08-06 01:36:11 +07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2013-02-28 08:04:42 +07:00
|
|
|
idr_preload(GFP_KERNEL);
|
2007-08-06 01:36:11 +07:00
|
|
|
write_lock_irqsave(&sg_index_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-02-28 08:04:42 +07:00
|
|
|
error = idr_alloc(&sg_index_idr, sdp, 0, SG_MAX_DEVS, GFP_NOWAIT);
|
|
|
|
if (error < 0) {
|
|
|
|
if (error == -ENOSPC) {
|
|
|
|
sdev_printk(KERN_WARNING, scsidp,
|
|
|
|
"Unable to attach sg device type=%d, minor number exceeds %d\n",
|
|
|
|
scsidp->type, SG_MAX_DEVS - 1);
|
|
|
|
error = -ENODEV;
|
|
|
|
} else {
|
2014-06-25 19:08:03 +07:00
|
|
|
sdev_printk(KERN_WARNING, scsidp, "%s: idr "
|
|
|
|
"allocation Sg_device failure: %d\n",
|
|
|
|
__func__, error);
|
2013-02-28 08:04:42 +07:00
|
|
|
}
|
|
|
|
goto out_unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2013-02-28 08:04:42 +07:00
|
|
|
k = error;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp,
|
|
|
|
"sg_alloc: dev=%d \n", k));
|
2005-04-17 05:20:36 +07:00
|
|
|
sprintf(disk->disk_name, "sg%d", k);
|
|
|
|
disk->first_minor = k;
|
|
|
|
sdp->disk = disk;
|
|
|
|
sdp->device = scsidp;
|
2014-06-25 19:08:03 +07:00
|
|
|
mutex_init(&sdp->open_rel_lock);
|
2009-02-16 11:26:53 +07:00
|
|
|
INIT_LIST_HEAD(&sdp->sfds);
|
2014-06-25 19:08:03 +07:00
|
|
|
init_waitqueue_head(&sdp->open_wait);
|
|
|
|
atomic_set(&sdp->detaching, 0);
|
|
|
|
rwlock_init(&sdp->sfd_lock);
|
2010-02-26 12:20:39 +07:00
|
|
|
sdp->sg_tablesize = queue_max_segments(q);
|
2007-08-06 01:36:11 +07:00
|
|
|
sdp->index = k;
|
2009-01-22 02:45:50 +07:00
|
|
|
kref_init(&sdp->d_ref);
|
2013-02-28 08:04:42 +07:00
|
|
|
error = 0;
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2013-02-28 08:04:42 +07:00
|
|
|
out_unlock:
|
2009-01-22 02:45:50 +07:00
|
|
|
write_unlock_irqrestore(&sg_index_lock, iflags);
|
2013-02-28 08:04:42 +07:00
|
|
|
idr_preload_end();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-08-06 01:36:11 +07:00
|
|
|
if (error) {
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree(sdp);
|
2007-08-06 01:36:11 +07:00
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
return sdp;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-06-25 19:08:03 +07:00
|
|
|
sg_add_device(struct device *cl_dev, struct class_interface *cl_intf)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-02-22 06:13:36 +07:00
|
|
|
struct scsi_device *scsidp = to_scsi_device(cl_dev->parent);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct gendisk *disk;
|
|
|
|
Sg_device *sdp = NULL;
|
|
|
|
struct cdev * cdev = NULL;
|
2007-08-06 01:36:11 +07:00
|
|
|
int error;
|
2006-06-29 20:39:54 +07:00
|
|
|
unsigned long iflags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
disk = alloc_disk(1);
|
|
|
|
if (!disk) {
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_warn("%s: alloc_disk failed\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
disk->major = SCSI_GENERIC_MAJOR;
|
|
|
|
|
|
|
|
error = -ENOMEM;
|
|
|
|
cdev = cdev_alloc();
|
|
|
|
if (!cdev) {
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_warn("%s: cdev_alloc failed\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
cdev->owner = THIS_MODULE;
|
|
|
|
cdev->ops = &sg_fops;
|
|
|
|
|
2007-08-06 01:36:11 +07:00
|
|
|
sdp = sg_alloc(disk, scsidp);
|
|
|
|
if (IS_ERR(sdp)) {
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_warn("%s: sg_alloc failed\n", __func__);
|
2007-08-06 01:36:11 +07:00
|
|
|
error = PTR_ERR(sdp);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-08-06 01:36:11 +07:00
|
|
|
error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 1);
|
2006-01-19 07:17:46 +07:00
|
|
|
if (error)
|
2006-06-29 20:39:54 +07:00
|
|
|
goto cdev_add_err;
|
2006-01-19 07:17:46 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sdp->cdev = cdev;
|
|
|
|
if (sg_sysfs_valid) {
|
2008-02-22 06:13:36 +07:00
|
|
|
struct device *sg_class_member;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-22 10:03:34 +07:00
|
|
|
sg_class_member = device_create(sg_sysfs_class, cl_dev->parent,
|
|
|
|
MKDEV(SCSI_GENERIC_MAJOR,
|
|
|
|
sdp->index),
|
|
|
|
sdp, "%s", disk->disk_name);
|
2008-01-15 11:18:00 +07:00
|
|
|
if (IS_ERR(sg_class_member)) {
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_err("%s: device_create failed\n", __func__);
|
2008-01-15 11:18:00 +07:00
|
|
|
error = PTR_ERR(sg_class_member);
|
|
|
|
goto cdev_add_err;
|
|
|
|
}
|
|
|
|
error = sysfs_create_link(&scsidp->sdev_gendev.kobj,
|
2005-04-17 05:20:36 +07:00
|
|
|
&sg_class_member->kobj, "generic");
|
|
|
|
if (error)
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_err("%s: unable to make symlink 'generic' back "
|
|
|
|
"to sg%d\n", __func__, sdp->index);
|
2005-04-17 05:20:36 +07:00
|
|
|
} else
|
2014-06-25 19:08:03 +07:00
|
|
|
pr_warn("%s: sg_sys Invalid\n", __func__);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d "
|
|
|
|
"type %d\n", sdp->index, scsidp->type);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-02-22 06:13:36 +07:00
|
|
|
dev_set_drvdata(cl_dev, sdp);
|
2008-01-15 11:17:47 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
2006-06-29 20:39:54 +07:00
|
|
|
cdev_add_err:
|
2007-08-06 01:36:11 +07:00
|
|
|
write_lock_irqsave(&sg_index_lock, iflags);
|
|
|
|
idr_remove(&sg_index_idr, sdp->index);
|
|
|
|
write_unlock_irqrestore(&sg_index_lock, iflags);
|
|
|
|
kfree(sdp);
|
2006-06-29 20:39:54 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
out:
|
|
|
|
put_disk(disk);
|
|
|
|
if (cdev)
|
|
|
|
cdev_del(cdev);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
|
|
|
sg_device_destroy(struct kref *kref)
|
2009-01-22 02:45:50 +07:00
|
|
|
{
|
|
|
|
struct sg_device *sdp = container_of(kref, struct sg_device, d_ref);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* CAUTION! Note that the device can still be found via idr_find()
|
|
|
|
* even though the refcount is 0. Therefore, do idr_remove() BEFORE
|
|
|
|
* any other cleanup.
|
|
|
|
*/
|
|
|
|
|
|
|
|
write_lock_irqsave(&sg_index_lock, flags);
|
|
|
|
idr_remove(&sg_index_idr, sdp->index);
|
|
|
|
write_unlock_irqrestore(&sg_index_lock, flags);
|
|
|
|
|
|
|
|
SCSI_LOG_TIMEOUT(3,
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_printk(KERN_INFO, sdp, "sg_device_destroy\n"));
|
2009-01-22 02:45:50 +07:00
|
|
|
|
|
|
|
put_disk(sdp->disk);
|
|
|
|
kfree(sdp);
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
|
|
|
sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-02-22 06:13:36 +07:00
|
|
|
struct scsi_device *scsidp = to_scsi_device(cl_dev->parent);
|
|
|
|
Sg_device *sdp = dev_get_drvdata(cl_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long iflags;
|
|
|
|
Sg_fd *sfp;
|
2014-06-25 19:08:03 +07:00
|
|
|
int val;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
if (!sdp)
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2014-06-25 19:08:03 +07:00
|
|
|
/* want sdp->detaching non-zero as soon as possible */
|
|
|
|
val = atomic_inc_return(&sdp->detaching);
|
|
|
|
if (val > 1)
|
|
|
|
return; /* only want to do following once per device */
|
2007-08-06 01:36:11 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"%s\n", __func__));
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
read_lock_irqsave(&sdp->sfd_lock, iflags);
|
2009-02-16 11:26:53 +07:00
|
|
|
list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) {
|
2014-06-25 19:08:03 +07:00
|
|
|
wake_up_interruptible_all(&sfp->read_wait);
|
2009-01-22 02:45:50 +07:00
|
|
|
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
|
2007-08-06 01:36:11 +07:00
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
wake_up_interruptible_all(&sdp->open_wait);
|
|
|
|
read_unlock_irqrestore(&sdp->sfd_lock, iflags);
|
2007-08-06 01:36:11 +07:00
|
|
|
|
|
|
|
sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
|
2008-02-22 06:13:36 +07:00
|
|
|
device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index));
|
2007-08-06 01:36:11 +07:00
|
|
|
cdev_del(sdp->cdev);
|
|
|
|
sdp->cdev = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
kref_put(&sdp->d_ref, sg_device_destroy);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR);
|
|
|
|
module_param_named(def_reserved_size, def_reserved_size, int,
|
|
|
|
S_IRUGO | S_IWUSR);
|
2005-04-17 05:20:36 +07:00
|
|
|
module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Douglas Gilbert");
|
|
|
|
MODULE_DESCRIPTION("SCSI generic (sg) driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(SG_VERSION_STR);
|
2006-03-08 15:14:20 +07:00
|
|
|
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element "
|
|
|
|
"size (default: max(SG_SCATTER_SZ, PAGE_SIZE))");
|
2005-04-17 05:20:36 +07:00
|
|
|
MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
|
|
|
|
MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))");
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
init_sg(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
if (scatter_elem_sz < PAGE_SIZE) {
|
|
|
|
scatter_elem_sz = PAGE_SIZE;
|
|
|
|
scatter_elem_sz_prev = scatter_elem_sz;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (def_reserved_size >= 0)
|
|
|
|
sg_big_buff = def_reserved_size;
|
2006-09-21 05:20:49 +07:00
|
|
|
else
|
|
|
|
def_reserved_size = sg_big_buff;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
|
|
|
|
SG_MAX_DEVS, "sg");
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2005-03-24 00:55:22 +07:00
|
|
|
sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic");
|
2005-04-17 05:20:36 +07:00
|
|
|
if ( IS_ERR(sg_sysfs_class) ) {
|
|
|
|
rc = PTR_ERR(sg_sysfs_class);
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
sg_sysfs_valid = 1;
|
|
|
|
rc = scsi_register_interface(&sg_interface);
|
|
|
|
if (0 == rc) {
|
|
|
|
#ifdef CONFIG_SCSI_PROC_FS
|
|
|
|
sg_proc_init();
|
|
|
|
#endif /* CONFIG_SCSI_PROC_FS */
|
|
|
|
return 0;
|
|
|
|
}
|
2005-03-24 00:55:22 +07:00
|
|
|
class_destroy(sg_sysfs_class);
|
2005-04-17 05:20:36 +07:00
|
|
|
err_out:
|
|
|
|
unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit
|
|
|
|
exit_sg(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_SCSI_PROC_FS
|
2018-04-11 16:26:22 +07:00
|
|
|
remove_proc_subtree("scsi/sg", NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif /* CONFIG_SCSI_PROC_FS */
|
|
|
|
scsi_unregister_interface(&sg_interface);
|
2005-03-24 00:55:22 +07:00
|
|
|
class_destroy(sg_sysfs_class);
|
2005-04-17 05:20:36 +07:00
|
|
|
sg_sysfs_valid = 0;
|
|
|
|
unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
|
|
|
|
SG_MAX_DEVS);
|
2007-08-06 01:36:11 +07:00
|
|
|
idr_destroy(&sg_index_idr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static int
|
|
|
|
sg_start_req(Sg_request *srp, unsigned char *cmd)
|
2008-08-28 14:17:07 +07:00
|
|
|
{
|
2008-09-02 20:50:05 +07:00
|
|
|
int res;
|
2008-08-28 14:17:07 +07:00
|
|
|
struct request *rq;
|
2017-01-27 15:46:29 +07:00
|
|
|
struct scsi_request *req;
|
2008-09-02 20:50:04 +07:00
|
|
|
Sg_fd *sfp = srp->parentfp;
|
|
|
|
sg_io_hdr_t *hp = &srp->header;
|
|
|
|
int dxfer_len = (int) hp->dxfer_len;
|
|
|
|
int dxfer_dir = hp->dxfer_direction;
|
2008-09-02 20:50:05 +07:00
|
|
|
unsigned int iov_count = hp->iovec_count;
|
2008-09-02 20:50:04 +07:00
|
|
|
Sg_scatter_hold *req_schp = &srp->data;
|
|
|
|
Sg_scatter_hold *rsv_schp = &sfp->reserve;
|
|
|
|
struct request_queue *q = sfp->parentdp->device->request_queue;
|
2008-09-02 20:50:05 +07:00
|
|
|
struct rq_map_data *md, map_data;
|
2008-08-28 14:17:07 +07:00
|
|
|
int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ;
|
2014-06-04 00:18:18 +07:00
|
|
|
unsigned char *long_cmdp = NULL;
|
2008-08-28 14:17:07 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_start_req: dxfer_len=%d\n",
|
|
|
|
dxfer_len));
|
2008-09-02 20:50:04 +07:00
|
|
|
|
2014-06-04 00:18:18 +07:00
|
|
|
if (hp->cmd_len > BLK_MAX_CDB) {
|
|
|
|
long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL);
|
|
|
|
if (!long_cmdp)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-02-14 00:10:58 +07:00
|
|
|
/*
|
|
|
|
* NOTE
|
|
|
|
*
|
|
|
|
* With scsi-mq enabled, there are a fixed number of preallocated
|
|
|
|
* requests equal in number to shost->can_queue. If all of the
|
2018-07-13 05:09:21 +07:00
|
|
|
* preallocated requests are already in use, then blk_get_request()
|
|
|
|
* will sleep until an active command completes, freeing up a request.
|
|
|
|
* Although waiting in an asynchronous interface is less than ideal, we
|
|
|
|
* do not want to use BLK_MQ_REQ_NOWAIT here because userspace might
|
|
|
|
* not expect an EWOULDBLOCK from this condition.
|
2015-02-14 00:10:58 +07:00
|
|
|
*/
|
2017-01-31 22:57:31 +07:00
|
|
|
rq = blk_get_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ?
|
2018-05-09 14:54:05 +07:00
|
|
|
REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0);
|
2014-08-28 21:15:21 +07:00
|
|
|
if (IS_ERR(rq)) {
|
2014-06-04 00:18:18 +07:00
|
|
|
kfree(long_cmdp);
|
2014-08-28 21:15:21 +07:00
|
|
|
return PTR_ERR(rq);
|
2014-06-04 00:18:18 +07:00
|
|
|
}
|
2017-01-27 15:46:29 +07:00
|
|
|
req = scsi_req(rq);
|
2008-08-28 14:17:07 +07:00
|
|
|
|
2014-06-04 00:18:18 +07:00
|
|
|
if (hp->cmd_len > BLK_MAX_CDB)
|
2017-01-27 15:46:29 +07:00
|
|
|
req->cmd = long_cmdp;
|
|
|
|
memcpy(req->cmd, cmd, hp->cmd_len);
|
|
|
|
req->cmd_len = hp->cmd_len;
|
2008-08-28 14:17:07 +07:00
|
|
|
|
|
|
|
srp->rq = rq;
|
|
|
|
rq->end_io_data = srp;
|
2017-04-06 00:18:12 +07:00
|
|
|
req->retries = SG_DEFAULT_RETRIES;
|
2008-08-28 14:17:07 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
|
2008-08-29 17:32:18 +07:00
|
|
|
return 0;
|
2008-08-28 14:17:07 +07:00
|
|
|
|
2008-09-02 20:50:05 +07:00
|
|
|
if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO &&
|
|
|
|
dxfer_dir != SG_DXFER_UNKNOWN && !iov_count &&
|
|
|
|
!sfp->parentdp->device->host->unchecked_isa_dma &&
|
2010-09-16 10:55:57 +07:00
|
|
|
blk_rq_aligned(q, (unsigned long)hp->dxferp, dxfer_len))
|
2008-09-02 20:50:05 +07:00
|
|
|
md = NULL;
|
|
|
|
else
|
|
|
|
md = &map_data;
|
|
|
|
|
|
|
|
if (md) {
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_lock(&sfp->f_mutex);
|
|
|
|
if (dxfer_len <= rsv_schp->bufflen &&
|
|
|
|
!sfp->res_in_use) {
|
|
|
|
sfp->res_in_use = 1;
|
2008-09-02 20:50:05 +07:00
|
|
|
sg_link_reserve(sfp, srp, dxfer_len);
|
2017-08-16 11:48:43 +07:00
|
|
|
} else if (hp->flags & SG_FLAG_MMAP_IO) {
|
|
|
|
res = -EBUSY; /* sfp->res_in_use == 1 */
|
|
|
|
if (dxfer_len > rsv_schp->bufflen)
|
|
|
|
res = -ENOMEM;
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2017-08-16 11:48:43 +07:00
|
|
|
return res;
|
2017-04-07 14:34:14 +07:00
|
|
|
} else {
|
2008-09-02 20:50:05 +07:00
|
|
|
res = sg_build_indirect(req_schp, sfp, dxfer_len);
|
2017-04-07 14:34:14 +07:00
|
|
|
if (res) {
|
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2008-09-02 20:50:05 +07:00
|
|
|
return res;
|
2017-04-07 14:34:14 +07:00
|
|
|
}
|
2008-09-02 20:50:05 +07:00
|
|
|
}
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_unlock(&sfp->f_mutex);
|
2008-09-02 20:50:02 +07:00
|
|
|
|
2008-09-02 20:50:05 +07:00
|
|
|
md->pages = req_schp->pages;
|
|
|
|
md->page_order = req_schp->page_order;
|
|
|
|
md->nr_entries = req_schp->k_use_sg;
|
2008-12-18 12:49:37 +07:00
|
|
|
md->offset = 0;
|
2008-12-18 12:49:38 +07:00
|
|
|
md->null_mapped = hp->dxferp ? 0 : 1;
|
2009-07-09 19:46:53 +07:00
|
|
|
if (dxfer_dir == SG_DXFER_TO_FROM_DEV)
|
|
|
|
md->from_user = 1;
|
|
|
|
else
|
|
|
|
md->from_user = 0;
|
2008-09-02 20:50:05 +07:00
|
|
|
}
|
|
|
|
|
2009-04-03 07:12:20 +07:00
|
|
|
if (iov_count) {
|
2015-03-22 07:25:30 +07:00
|
|
|
struct iovec *iov = NULL;
|
2015-01-18 22:16:31 +07:00
|
|
|
struct iov_iter i;
|
2009-04-03 07:12:20 +07:00
|
|
|
|
2019-03-14 23:45:18 +07:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
if (in_compat_syscall())
|
|
|
|
res = compat_import_iovec(rw, hp->dxferp, iov_count,
|
|
|
|
0, &iov, &i);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
res = import_iovec(rw, hp->dxferp, iov_count,
|
|
|
|
0, &iov, &i);
|
2015-03-22 07:25:30 +07:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
2009-04-03 07:12:20 +07:00
|
|
|
|
2015-03-22 07:25:30 +07:00
|
|
|
iov_iter_truncate(&i, hp->dxfer_len);
|
2017-02-19 14:15:27 +07:00
|
|
|
if (!iov_iter_count(&i)) {
|
|
|
|
kfree(iov);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2009-04-03 07:12:20 +07:00
|
|
|
|
2015-01-18 22:16:31 +07:00
|
|
|
res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC);
|
2009-04-03 07:12:20 +07:00
|
|
|
kfree(iov);
|
|
|
|
} else
|
2008-09-02 20:50:05 +07:00
|
|
|
res = blk_rq_map_user(q, rq, md, hp->dxferp,
|
|
|
|
hp->dxfer_len, GFP_ATOMIC);
|
2008-08-29 17:32:18 +07:00
|
|
|
|
|
|
|
if (!res) {
|
2008-09-02 20:50:05 +07:00
|
|
|
srp->bio = rq->bio;
|
2008-08-29 17:32:18 +07:00
|
|
|
|
2008-09-02 20:50:05 +07:00
|
|
|
if (!md) {
|
|
|
|
req_schp->dio_in_use = 1;
|
|
|
|
hp->info |= SG_INFO_DIRECT_IO;
|
|
|
|
}
|
|
|
|
}
|
2008-08-29 17:32:18 +07:00
|
|
|
return res;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static int
|
|
|
|
sg_finish_rem_req(Sg_request *srp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-04-03 22:35:42 +07:00
|
|
|
int ret = 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_fd *sfp = srp->parentfp;
|
|
|
|
Sg_scatter_hold *req_schp = &srp->data;
|
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_finish_rem_req: res_used=%d\n",
|
|
|
|
(int) srp->res_used));
|
2015-02-14 00:09:44 +07:00
|
|
|
if (srp->bio)
|
|
|
|
ret = blk_rq_unmap_user(srp->bio);
|
2008-08-29 17:32:18 +07:00
|
|
|
|
2015-02-14 00:09:44 +07:00
|
|
|
if (srp->rq) {
|
2017-01-27 15:46:29 +07:00
|
|
|
scsi_req_free_cmd(scsi_req(srp->rq));
|
2008-08-28 14:17:07 +07:00
|
|
|
blk_put_request(srp->rq);
|
2008-08-28 14:17:08 +07:00
|
|
|
}
|
2008-08-28 14:17:07 +07:00
|
|
|
|
2009-09-17 14:10:14 +07:00
|
|
|
if (srp->res_used)
|
|
|
|
sg_unlink_reserve(sfp, srp);
|
|
|
|
else
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_remove_scat(sfp, req_schp);
|
2009-09-17 14:10:14 +07:00
|
|
|
|
2009-04-03 22:35:42 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize)
|
|
|
|
{
|
2008-08-29 17:32:18 +07:00
|
|
|
int sg_bufflen = tablesize * sizeof(struct page *);
|
2006-02-01 18:31:40 +07:00
|
|
|
gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
schp->pages = kzalloc(sg_bufflen, gfp_flags);
|
|
|
|
if (!schp->pages)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
schp->sglist_len = sg_bufflen;
|
2005-11-08 17:06:41 +07:00
|
|
|
return tablesize; /* number of scat_gath elements allocated */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
|
|
|
|
{
|
2008-08-29 17:32:18 +07:00
|
|
|
int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems;
|
2005-11-08 17:06:41 +07:00
|
|
|
int sg_tablesize = sfp->parentdp->sg_tablesize;
|
2008-08-29 17:32:18 +07:00
|
|
|
int blk_size = buff_size, order;
|
2018-06-18 20:57:12 +07:00
|
|
|
gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO;
|
2017-04-07 14:34:12 +07:00
|
|
|
struct sg_device *sdp = sfp->parentdp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-05-24 04:41:36 +07:00
|
|
|
if (blk_size < 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
if (0 == blk_size)
|
|
|
|
++blk_size; /* don't know why */
|
2009-02-12 00:42:57 +07:00
|
|
|
/* round request up to next highest SG_SECTOR_SZ byte boundary */
|
|
|
|
blk_size = ALIGN(blk_size, SG_SECTOR_SZ);
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_build_indirect: buff_size=%d, blk_size=%d\n",
|
|
|
|
buff_size, blk_size));
|
2005-11-08 17:06:41 +07:00
|
|
|
|
|
|
|
/* N.B. ret_sz carried into this block ... */
|
|
|
|
mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize);
|
|
|
|
if (mx_sc_elems < 0)
|
|
|
|
return mx_sc_elems; /* most likely -ENOMEM */
|
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
num = scatter_elem_sz;
|
|
|
|
if (unlikely(num != scatter_elem_sz_prev)) {
|
|
|
|
if (num < PAGE_SIZE) {
|
|
|
|
scatter_elem_sz = PAGE_SIZE;
|
|
|
|
scatter_elem_sz_prev = PAGE_SIZE;
|
|
|
|
} else
|
|
|
|
scatter_elem_sz_prev = num;
|
|
|
|
}
|
2008-08-29 17:32:18 +07:00
|
|
|
|
2017-04-07 14:34:12 +07:00
|
|
|
if (sdp->device->host->unchecked_isa_dma)
|
2008-08-29 17:32:18 +07:00
|
|
|
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) {
|
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
num = (rem_sz > scatter_elem_sz_prev) ?
|
2008-08-29 17:32:18 +07:00
|
|
|
scatter_elem_sz_prev : rem_sz;
|
|
|
|
|
2018-06-18 20:57:12 +07:00
|
|
|
schp->pages[k] = alloc_pages(gfp_mask, order);
|
2008-08-29 17:32:18 +07:00
|
|
|
if (!schp->pages[k])
|
|
|
|
goto out;
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2006-09-21 05:20:49 +07:00
|
|
|
if (num == scatter_elem_sz_prev) {
|
|
|
|
if (unlikely(ret_sz > scatter_elem_sz_prev)) {
|
|
|
|
scatter_elem_sz = ret_sz;
|
|
|
|
scatter_elem_sz_prev = ret_sz;
|
|
|
|
}
|
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_build_indirect: k=%d, num=%d, ret_sz=%d\n",
|
|
|
|
k, num, ret_sz));
|
2005-11-08 17:06:41 +07:00
|
|
|
} /* end of for loop */
|
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
schp->page_order = order;
|
2005-11-08 17:06:41 +07:00
|
|
|
schp->k_use_sg = k;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_build_indirect: k_use_sg=%d, rem_sz=%d\n",
|
|
|
|
k, rem_sz));
|
2005-11-08 17:06:41 +07:00
|
|
|
|
|
|
|
schp->bufflen = blk_size;
|
|
|
|
if (rem_sz > 0) /* must have failed */
|
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
2008-08-29 17:32:18 +07:00
|
|
|
out:
|
|
|
|
for (i = 0; i < k; i++)
|
2009-09-03 19:27:08 +07:00
|
|
|
__free_pages(schp->pages[i], order);
|
2008-08-29 17:32:18 +07:00
|
|
|
|
|
|
|
if (--order >= 0)
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg));
|
2008-08-29 17:32:18 +07:00
|
|
|
if (schp->pages && schp->sglist_len > 0) {
|
2008-08-28 14:17:08 +07:00
|
|
|
if (!schp->dio_in_use) {
|
2005-04-17 05:20:36 +07:00
|
|
|
int k;
|
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) {
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(5,
|
|
|
|
sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_remove_scat: k=%d, pg=0x%p\n",
|
|
|
|
k, schp->pages[k]));
|
2008-08-29 17:32:18 +07:00
|
|
|
__free_pages(schp->pages[k], schp->page_order);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-08-28 14:17:08 +07:00
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
kfree(schp->pages);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
memset(schp, 0, sizeof (*schp));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer)
|
|
|
|
{
|
|
|
|
Sg_scatter_hold *schp = &srp->data;
|
2005-11-08 17:06:41 +07:00
|
|
|
int k, num;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp,
|
|
|
|
"sg_read_oxfer: num_read_xfer=%d\n",
|
|
|
|
num_read_xfer));
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((!outp) || (num_read_xfer <= 0))
|
|
|
|
return 0;
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
num = 1 << (PAGE_SHIFT + schp->page_order);
|
|
|
|
for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) {
|
2005-11-08 17:06:41 +07:00
|
|
|
if (num > num_read_xfer) {
|
2019-10-18 02:39:23 +07:00
|
|
|
if (copy_to_user(outp, page_address(schp->pages[k]),
|
2005-11-08 17:06:41 +07:00
|
|
|
num_read_xfer))
|
|
|
|
return -EFAULT;
|
|
|
|
break;
|
|
|
|
} else {
|
2019-10-18 02:39:23 +07:00
|
|
|
if (copy_to_user(outp, page_address(schp->pages[k]),
|
2005-11-08 17:06:41 +07:00
|
|
|
num))
|
|
|
|
return -EFAULT;
|
|
|
|
num_read_xfer -= num;
|
|
|
|
if (num_read_xfer <= 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2005-11-08 17:06:41 +07:00
|
|
|
outp += num;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sg_build_reserve(Sg_fd * sfp, int req_size)
|
|
|
|
{
|
|
|
|
Sg_scatter_hold *schp = &sfp->reserve;
|
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_build_reserve: req_size=%d\n", req_size));
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
if (req_size < PAGE_SIZE)
|
|
|
|
req_size = PAGE_SIZE;
|
|
|
|
if (0 == sg_build_indirect(schp, sfp, req_size))
|
|
|
|
return;
|
|
|
|
else
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_remove_scat(sfp, schp);
|
2005-04-17 05:20:36 +07:00
|
|
|
req_size >>= 1; /* divide by 2 */
|
|
|
|
} while (req_size > (PAGE_SIZE / 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size)
|
|
|
|
{
|
|
|
|
Sg_scatter_hold *req_schp = &srp->data;
|
|
|
|
Sg_scatter_hold *rsv_schp = &sfp->reserve;
|
2005-11-08 17:06:41 +07:00
|
|
|
int k, num, rem;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
srp->res_used = 1;
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_link_reserve: size=%d\n", size));
|
2006-02-15 01:42:24 +07:00
|
|
|
rem = size;
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2008-08-29 17:32:18 +07:00
|
|
|
num = 1 << (PAGE_SHIFT + rsv_schp->page_order);
|
|
|
|
for (k = 0; k < rsv_schp->k_use_sg; k++) {
|
2005-11-08 17:06:41 +07:00
|
|
|
if (rem <= num) {
|
|
|
|
req_schp->k_use_sg = k + 1;
|
|
|
|
req_schp->sglist_len = rsv_schp->sglist_len;
|
2008-08-29 17:32:18 +07:00
|
|
|
req_schp->pages = rsv_schp->pages;
|
2005-11-08 17:06:41 +07:00
|
|
|
|
|
|
|
req_schp->bufflen = size;
|
2008-08-29 17:32:18 +07:00
|
|
|
req_schp->page_order = rsv_schp->page_order;
|
2005-11-08 17:06:41 +07:00
|
|
|
break;
|
|
|
|
} else
|
|
|
|
rem -= num;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-11-08 17:06:41 +07:00
|
|
|
|
|
|
|
if (k >= rsv_schp->k_use_sg)
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp,
|
|
|
|
"sg_link_reserve: BAD size\n"));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp)
|
|
|
|
{
|
|
|
|
Sg_scatter_hold *req_schp = &srp->data;
|
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp,
|
|
|
|
"sg_unlink_reserve: req->k_use_sg=%d\n",
|
|
|
|
(int) req_schp->k_use_sg));
|
2005-04-17 05:20:36 +07:00
|
|
|
req_schp->k_use_sg = 0;
|
|
|
|
req_schp->bufflen = 0;
|
2008-08-29 17:32:18 +07:00
|
|
|
req_schp->pages = NULL;
|
|
|
|
req_schp->page_order = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
req_schp->sglist_len = 0;
|
|
|
|
srp->res_used = 0;
|
2017-04-24 15:26:36 +07:00
|
|
|
/* Called without mutex lock to avoid deadlock */
|
|
|
|
sfp->res_in_use = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Sg_request *
|
|
|
|
sg_get_rq_mark(Sg_fd * sfp, int pack_id)
|
|
|
|
{
|
|
|
|
Sg_request *resp;
|
|
|
|
unsigned long iflags;
|
|
|
|
|
|
|
|
write_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
list_for_each_entry(resp, &sfp->rq_list, entry) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* 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 */
|
2017-05-10 14:53:40 +07:00
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
return resp;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
2017-05-10 14:53:40 +07:00
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* always adds to end of list */
|
|
|
|
static Sg_request *
|
|
|
|
sg_add_request(Sg_fd * sfp)
|
|
|
|
{
|
|
|
|
int k;
|
|
|
|
unsigned long iflags;
|
|
|
|
Sg_request *rp = sfp->req_arr;
|
|
|
|
|
|
|
|
write_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2017-04-07 14:34:16 +07:00
|
|
|
if (k >= SG_MAX_QUEUE)
|
|
|
|
goto out_unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2017-04-07 14:34:16 +07:00
|
|
|
memset(rp, 0, sizeof (Sg_request));
|
|
|
|
rp->parentfp = sfp;
|
|
|
|
rp->header.duration = jiffies_to_msecs(jiffies);
|
|
|
|
list_add_tail(&rp->entry, &sfp->rq_list);
|
2005-04-17 05:20:36 +07:00
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
return rp;
|
|
|
|
out_unlock:
|
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return of 1 for found; 0 for not found */
|
|
|
|
static int
|
|
|
|
sg_remove_request(Sg_fd * sfp, Sg_request * srp)
|
|
|
|
{
|
|
|
|
unsigned long iflags;
|
|
|
|
int res = 0;
|
|
|
|
|
2017-04-07 14:34:16 +07:00
|
|
|
if (!sfp || !srp || list_empty(&sfp->rq_list))
|
2005-04-17 05:20:36 +07:00
|
|
|
return res;
|
|
|
|
write_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
if (!list_empty(&srp->entry)) {
|
|
|
|
list_del(&srp->entry);
|
|
|
|
srp->parentfp = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
res = 1;
|
|
|
|
}
|
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Sg_fd *
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_add_sfp(Sg_device * sdp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
Sg_fd *sfp;
|
|
|
|
unsigned long iflags;
|
2007-02-20 23:01:57 +07:00
|
|
|
int bufflen;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-08 17:06:41 +07:00
|
|
|
sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!sfp)
|
2014-06-25 19:08:03 +07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2005-11-08 17:06:41 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
init_waitqueue_head(&sfp->read_wait);
|
|
|
|
rwlock_init(&sfp->rq_list_lock);
|
2017-04-07 14:34:16 +07:00
|
|
|
INIT_LIST_HEAD(&sfp->rq_list);
|
2009-01-22 02:45:50 +07:00
|
|
|
kref_init(&sfp->f_ref);
|
2017-04-07 14:34:14 +07:00
|
|
|
mutex_init(&sfp->f_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
sfp->timeout = SG_DEFAULT_TIMEOUT;
|
|
|
|
sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
|
|
|
|
sfp->force_packid = SG_DEF_FORCE_PACK_ID;
|
|
|
|
sfp->cmd_q = SG_DEF_COMMAND_Q;
|
|
|
|
sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
|
|
|
|
sfp->parentdp = sdp;
|
2014-06-25 19:08:03 +07:00
|
|
|
write_lock_irqsave(&sdp->sfd_lock, iflags);
|
|
|
|
if (atomic_read(&sdp->detaching)) {
|
|
|
|
write_unlock_irqrestore(&sdp->sfd_lock, iflags);
|
2018-07-13 03:30:45 +07:00
|
|
|
kfree(sfp);
|
2014-06-25 19:08:03 +07:00
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
}
|
2009-02-16 11:26:53 +07:00
|
|
|
list_add_tail(&sfp->sfd_siblings, &sdp->sfds);
|
2014-06-25 19:08:03 +07:00
|
|
|
write_unlock_irqrestore(&sdp->sfd_lock, iflags);
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_add_sfp: sfp=0x%p\n", sfp));
|
2006-09-21 05:20:49 +07:00
|
|
|
if (unlikely(sg_big_buff != def_reserved_size))
|
|
|
|
sg_big_buff = def_reserved_size;
|
|
|
|
|
2007-02-20 23:01:57 +07:00
|
|
|
bufflen = min_t(int, sg_big_buff,
|
2014-06-02 20:56:46 +07:00
|
|
|
max_sectors_bytes(sdp->device->request_queue));
|
2007-02-20 23:01:57 +07:00
|
|
|
sg_build_reserve(sfp, bufflen);
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_add_sfp: bufflen=%d, k_use_sg=%d\n",
|
|
|
|
sfp->reserve.bufflen,
|
|
|
|
sfp->reserve.k_use_sg));
|
2009-01-22 02:45:50 +07:00
|
|
|
|
|
|
|
kref_get(&sdp->d_ref);
|
|
|
|
__module_get(THIS_MODULE);
|
2005-04-17 05:20:36 +07:00
|
|
|
return sfp;
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
|
|
|
sg_remove_sfp_usercontext(struct work_struct *work)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-22 02:45:50 +07:00
|
|
|
struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
|
|
|
|
struct sg_device *sdp = sfp->parentdp;
|
2017-04-07 14:34:16 +07:00
|
|
|
Sg_request *srp;
|
2017-04-07 14:34:17 +07:00
|
|
|
unsigned long iflags;
|
2009-01-22 02:45:50 +07:00
|
|
|
|
|
|
|
/* Cleanup any responses which were never read(). */
|
2017-04-07 14:34:17 +07:00
|
|
|
write_lock_irqsave(&sfp->rq_list_lock, iflags);
|
2017-04-07 14:34:16 +07:00
|
|
|
while (!list_empty(&sfp->rq_list)) {
|
|
|
|
srp = list_first_entry(&sfp->rq_list, Sg_request, entry);
|
|
|
|
sg_finish_rem_req(srp);
|
2017-04-07 14:34:17 +07:00
|
|
|
list_del(&srp->entry);
|
|
|
|
srp->parentfp = NULL;
|
2017-04-07 14:34:16 +07:00
|
|
|
}
|
2017-04-07 14:34:17 +07:00
|
|
|
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (sfp->reserve.bufflen > 0) {
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_remove_sfp: bufflen=%d, k_use_sg=%d\n",
|
2009-01-22 02:45:50 +07:00
|
|
|
(int) sfp->reserve.bufflen,
|
|
|
|
(int) sfp->reserve.k_use_sg));
|
2014-06-25 21:39:55 +07:00
|
|
|
sg_remove_scat(sfp, &sfp->reserve);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-01-22 02:45:50 +07:00
|
|
|
|
2014-06-25 21:39:55 +07:00
|
|
|
SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
|
|
|
|
"sg_remove_sfp: sfp=0x%p\n", sfp));
|
2005-11-08 17:06:41 +07:00
|
|
|
kfree(sfp);
|
2009-01-22 02:45:50 +07:00
|
|
|
|
|
|
|
scsi_device_put(sdp->device);
|
2014-06-25 19:08:03 +07:00
|
|
|
kref_put(&sdp->d_ref, sg_device_destroy);
|
2009-01-22 02:45:50 +07:00
|
|
|
module_put(THIS_MODULE);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static void
|
|
|
|
sg_remove_sfp(struct kref *kref)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-22 02:45:50 +07:00
|
|
|
struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref);
|
2013-10-25 16:27:02 +07:00
|
|
|
struct sg_device *sdp = sfp->parentdp;
|
2009-01-22 02:45:50 +07:00
|
|
|
unsigned long iflags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
write_lock_irqsave(&sdp->sfd_lock, iflags);
|
2009-02-16 11:26:53 +07:00
|
|
|
list_del(&sfp->sfd_siblings);
|
2014-06-25 19:08:03 +07:00
|
|
|
write_unlock_irqrestore(&sdp->sfd_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-04-03 17:28:06 +07:00
|
|
|
INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext);
|
|
|
|
schedule_work(&sfp->ew.work);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SCSI_PROC_FS
|
2007-08-06 01:36:11 +07:00
|
|
|
static int
|
|
|
|
sg_idr_max_id(int id, void *p, void *data)
|
|
|
|
{
|
|
|
|
int *k = data;
|
|
|
|
|
|
|
|
if (*k < id)
|
|
|
|
*k = id;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
|
|
|
sg_last_dev(void)
|
|
|
|
{
|
2008-01-23 03:25:49 +07:00
|
|
|
int k = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long iflags;
|
|
|
|
|
2007-08-06 01:36:11 +07:00
|
|
|
read_lock_irqsave(&sg_index_lock, iflags);
|
|
|
|
idr_for_each(&sg_index_idr, sg_idr_max_id, &k);
|
|
|
|
read_unlock_irqrestore(&sg_index_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
return k + 1; /* origin 1 */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
/* must be called with sg_index_lock held */
|
|
|
|
static Sg_device *sg_lookup_dev(int dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-22 02:45:50 +07:00
|
|
|
return idr_find(&sg_index_idr, dev);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
static Sg_device *
|
|
|
|
sg_get_dev(int dev)
|
2009-01-22 02:45:50 +07:00
|
|
|
{
|
|
|
|
struct sg_device *sdp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
read_lock_irqsave(&sg_index_lock, flags);
|
|
|
|
sdp = sg_lookup_dev(dev);
|
|
|
|
if (!sdp)
|
|
|
|
sdp = ERR_PTR(-ENXIO);
|
2014-06-25 19:08:03 +07:00
|
|
|
else if (atomic_read(&sdp->detaching)) {
|
|
|
|
/* If sdp->detaching, then the refcount may already be 0, in
|
2009-01-22 02:45:50 +07:00
|
|
|
* which case it would be a bug to do kref_get().
|
|
|
|
*/
|
|
|
|
sdp = ERR_PTR(-ENODEV);
|
|
|
|
} else
|
|
|
|
kref_get(&sdp->d_ref);
|
|
|
|
read_unlock_irqrestore(&sg_index_lock, flags);
|
2007-08-06 01:36:11 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return sdp;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SCSI_PROC_FS
|
|
|
|
static int sg_proc_seq_show_int(struct seq_file *s, void *v);
|
|
|
|
|
|
|
|
static int sg_proc_single_open_adio(struct inode *inode, struct file *file);
|
|
|
|
static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer,
|
|
|
|
size_t count, loff_t *off);
|
2009-10-02 05:43:56 +07:00
|
|
|
static const struct file_operations adio_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = sg_proc_single_open_adio,
|
2009-10-02 05:43:56 +07:00
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2005-04-17 05:20:36 +07:00
|
|
|
.write = sg_proc_write_adio,
|
|
|
|
.release = single_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sg_proc_single_open_dressz(struct inode *inode, struct file *file);
|
|
|
|
static ssize_t sg_proc_write_dressz(struct file *filp,
|
|
|
|
const char __user *buffer, size_t count, loff_t *off);
|
2009-10-02 05:43:56 +07:00
|
|
|
static const struct file_operations dressz_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = sg_proc_single_open_dressz,
|
2009-10-02 05:43:56 +07:00
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2005-04-17 05:20:36 +07:00
|
|
|
.write = sg_proc_write_dressz,
|
|
|
|
.release = single_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_version(struct seq_file *s, void *v);
|
|
|
|
static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v);
|
|
|
|
static int sg_proc_seq_show_dev(struct seq_file *s, void *v);
|
|
|
|
static void * dev_seq_start(struct seq_file *s, loff_t *pos);
|
|
|
|
static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos);
|
|
|
|
static void dev_seq_stop(struct seq_file *s, void *v);
|
2009-09-23 06:43:43 +07:00
|
|
|
static const struct seq_operations dev_seq_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.start = dev_seq_start,
|
|
|
|
.next = dev_seq_next,
|
|
|
|
.stop = dev_seq_stop,
|
|
|
|
.show = sg_proc_seq_show_dev,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v);
|
2009-09-23 06:43:43 +07:00
|
|
|
static const struct seq_operations devstrs_seq_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.start = dev_seq_start,
|
|
|
|
.next = dev_seq_next,
|
|
|
|
.stop = dev_seq_stop,
|
|
|
|
.show = sg_proc_seq_show_devstrs,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_debug(struct seq_file *s, void *v);
|
2009-09-23 06:43:43 +07:00
|
|
|
static const struct seq_operations debug_seq_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.start = dev_seq_start,
|
|
|
|
.next = dev_seq_next,
|
|
|
|
.stop = dev_seq_stop,
|
|
|
|
.show = sg_proc_seq_show_debug,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
sg_proc_init(void)
|
|
|
|
{
|
2018-04-11 16:26:22 +07:00
|
|
|
struct proc_dir_entry *p;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2018-04-11 16:26:22 +07:00
|
|
|
p = proc_mkdir("scsi/sg", NULL);
|
|
|
|
if (!p)
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
|
2018-04-11 16:26:22 +07:00
|
|
|
proc_create("allow_dio", S_IRUGO | S_IWUSR, p, &adio_fops);
|
|
|
|
proc_create_seq("debug", S_IRUGO, p, &debug_seq_ops);
|
|
|
|
proc_create("def_reserved_size", S_IRUGO | S_IWUSR, p, &dressz_fops);
|
|
|
|
proc_create_single("device_hdr", S_IRUGO, p, sg_proc_seq_show_devhdr);
|
|
|
|
proc_create_seq("devices", S_IRUGO, p, &dev_seq_ops);
|
|
|
|
proc_create_seq("device_strs", S_IRUGO, p, &devstrs_seq_ops);
|
|
|
|
proc_create_single("version", S_IRUGO, p, sg_proc_seq_show_version);
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_int(struct seq_file *s, void *v)
|
|
|
|
{
|
|
|
|
seq_printf(s, "%d\n", *((int *)s->private));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_single_open_adio(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, sg_proc_seq_show_int, &sg_allow_dio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sg_proc_write_adio(struct file *filp, const char __user *buffer,
|
|
|
|
size_t count, loff_t *off)
|
|
|
|
{
|
2012-01-11 06:42:34 +07:00
|
|
|
int err;
|
|
|
|
unsigned long num;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
|
|
|
|
return -EACCES;
|
2012-01-11 06:42:34 +07:00
|
|
|
err = kstrtoul_from_user(buffer, count, 0, &num);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
sg_allow_dio = num ? 1 : 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_single_open_dressz(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, sg_proc_seq_show_int, &sg_big_buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sg_proc_write_dressz(struct file *filp, const char __user *buffer,
|
|
|
|
size_t count, loff_t *off)
|
|
|
|
{
|
2012-01-11 06:42:34 +07:00
|
|
|
int err;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long k = ULONG_MAX;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
|
|
|
|
return -EACCES;
|
2012-01-11 06:42:34 +07:00
|
|
|
|
|
|
|
err = kstrtoul_from_user(buffer, count, 0, &k);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (k <= 1048576) { /* limit "big buff" to 1 MB */
|
|
|
|
sg_big_buff = k;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_version(struct seq_file *s, void *v)
|
|
|
|
{
|
|
|
|
seq_printf(s, "%d\t%s [%s]\n", sg_version_num, SG_VERSION_STR,
|
|
|
|
sg_version_date);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v)
|
|
|
|
{
|
2014-06-25 19:08:03 +07:00
|
|
|
seq_puts(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sg_proc_deviter {
|
|
|
|
loff_t index;
|
|
|
|
size_t max;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void * dev_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL);
|
|
|
|
|
2005-08-28 01:07:52 +07:00
|
|
|
s->private = it;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (! it)
|
|
|
|
return NULL;
|
2005-08-28 01:07:52 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
it->index = *pos;
|
|
|
|
it->max = sg_last_dev();
|
|
|
|
if (it->index >= it->max)
|
2005-08-28 01:07:52 +07:00
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
|
{
|
2005-08-28 01:07:52 +07:00
|
|
|
struct sg_proc_deviter * it = s->private;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
*pos = ++it->index;
|
|
|
|
return (it->index < it->max) ? it : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dev_seq_stop(struct seq_file *s, void *v)
|
|
|
|
{
|
2005-08-28 01:07:52 +07:00
|
|
|
kfree(s->private);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_dev(struct seq_file *s, void *v)
|
|
|
|
{
|
|
|
|
struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
|
|
|
|
Sg_device *sdp;
|
|
|
|
struct scsi_device *scsidp;
|
2009-01-22 02:45:50 +07:00
|
|
|
unsigned long iflags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
read_lock_irqsave(&sg_index_lock, iflags);
|
|
|
|
sdp = it ? sg_lookup_dev(it->index) : NULL;
|
2014-06-25 19:08:03 +07:00
|
|
|
if ((NULL == sdp) || (NULL == sdp->device) ||
|
|
|
|
(atomic_read(&sdp->detaching)))
|
|
|
|
seq_puts(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
|
|
|
|
else {
|
|
|
|
scsidp = sdp->device;
|
2014-06-25 20:27:36 +07:00
|
|
|
seq_printf(s, "%d\t%d\t%d\t%llu\t%d\t%d\t%d\t%d\t%d\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
scsidp->host->host_no, scsidp->channel,
|
|
|
|
scsidp->id, scsidp->lun, (int) scsidp->type,
|
|
|
|
1,
|
|
|
|
(int) scsidp->queue_depth,
|
2014-04-12 00:07:01 +07:00
|
|
|
(int) atomic_read(&scsidp->device_busy),
|
2005-04-17 05:20:36 +07:00
|
|
|
(int) scsi_device_online(scsidp));
|
2014-06-25 19:08:03 +07:00
|
|
|
}
|
2009-01-22 02:45:50 +07:00
|
|
|
read_unlock_irqrestore(&sg_index_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
|
|
|
|
{
|
|
|
|
struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
|
|
|
|
Sg_device *sdp;
|
|
|
|
struct scsi_device *scsidp;
|
2009-01-22 02:45:50 +07:00
|
|
|
unsigned long iflags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
read_lock_irqsave(&sg_index_lock, iflags);
|
|
|
|
sdp = it ? sg_lookup_dev(it->index) : NULL;
|
2014-06-25 19:08:03 +07:00
|
|
|
scsidp = sdp ? sdp->device : NULL;
|
|
|
|
if (sdp && scsidp && (!atomic_read(&sdp->detaching)))
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n",
|
|
|
|
scsidp->vendor, scsidp->model, scsidp->rev);
|
|
|
|
else
|
2014-06-25 19:08:03 +07:00
|
|
|
seq_puts(s, "<no active device>\n");
|
2009-01-22 02:45:50 +07:00
|
|
|
read_unlock_irqrestore(&sg_index_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-25 16:21:57 +07:00
|
|
|
/* must be called while holding sg_index_lock */
|
2005-04-17 05:20:36 +07:00
|
|
|
static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
|
|
|
|
{
|
2017-04-07 14:34:16 +07:00
|
|
|
int k, new_interface, blen, usg;
|
2005-04-17 05:20:36 +07:00
|
|
|
Sg_request *srp;
|
|
|
|
Sg_fd *fp;
|
|
|
|
const sg_io_hdr_t *hp;
|
|
|
|
const char * cp;
|
2005-04-03 02:51:23 +07:00
|
|
|
unsigned int ms;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-02-16 11:26:53 +07:00
|
|
|
k = 0;
|
|
|
|
list_for_each_entry(fp, &sdp->sfds, sfd_siblings) {
|
|
|
|
k++;
|
2009-01-22 02:45:50 +07:00
|
|
|
read_lock(&fp->rq_list_lock); /* irqs already disabled */
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(s, " FD(%d): timeout=%dms bufflen=%d "
|
2009-02-16 11:26:53 +07:00
|
|
|
"(res)sgat=%d low_dma=%d\n", k,
|
2005-04-17 05:20:36 +07:00
|
|
|
jiffies_to_msecs(fp->timeout),
|
|
|
|
fp->reserve.bufflen,
|
|
|
|
(int) fp->reserve.k_use_sg,
|
2017-04-07 14:34:12 +07:00
|
|
|
(int) sdp->device->host->unchecked_isa_dma);
|
2012-04-13 04:33:39 +07:00
|
|
|
seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
(int) fp->cmd_q, (int) fp->force_packid,
|
2012-04-13 04:33:39 +07:00
|
|
|
(int) fp->keep_orphan);
|
2017-04-07 14:34:16 +07:00
|
|
|
list_for_each_entry(srp, &fp->rq_list, entry) {
|
2005-04-17 05:20:36 +07:00
|
|
|
hp = &srp->header;
|
|
|
|
new_interface = (hp->interface_id == '\0') ? 0 : 1;
|
|
|
|
if (srp->res_used) {
|
2017-04-07 14:34:16 +07:00
|
|
|
if (new_interface &&
|
2005-04-17 05:20:36 +07:00
|
|
|
(SG_FLAG_MMAP_IO & hp->flags))
|
|
|
|
cp = " mmap>> ";
|
|
|
|
else
|
|
|
|
cp = " rb>> ";
|
|
|
|
} else {
|
|
|
|
if (SG_INFO_DIRECT_IO_MASK & hp->info)
|
|
|
|
cp = " dio>> ";
|
|
|
|
else
|
|
|
|
cp = " ";
|
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
seq_puts(s, cp);
|
2005-11-08 17:06:41 +07:00
|
|
|
blen = srp->data.bufflen;
|
|
|
|
usg = srp->data.k_use_sg;
|
2014-06-25 19:08:03 +07:00
|
|
|
seq_puts(s, srp->done ?
|
|
|
|
((1 == srp->done) ? "rcv:" : "fin:")
|
|
|
|
: "act:");
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(s, " id=%d blen=%d",
|
|
|
|
srp->header.pack_id, blen);
|
|
|
|
if (srp->done)
|
|
|
|
seq_printf(s, " dur=%d", hp->duration);
|
2005-04-03 02:51:23 +07:00
|
|
|
else {
|
|
|
|
ms = jiffies_to_msecs(jiffies);
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(s, " t_o/elap=%d/%d",
|
2005-04-03 02:51:23 +07:00
|
|
|
(new_interface ? hp->timeout :
|
|
|
|
jiffies_to_msecs(fp->timeout)),
|
|
|
|
(ms > hp->duration ? ms - hp->duration : 0));
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(s, "ms sgat=%d op=0x%02x\n", usg,
|
|
|
|
(int) srp->data.cmd_opcode);
|
|
|
|
}
|
2017-04-07 14:34:16 +07:00
|
|
|
if (list_empty(&fp->rq_list))
|
2014-06-25 19:08:03 +07:00
|
|
|
seq_puts(s, " No requests active\n");
|
2009-01-22 02:45:50 +07:00
|
|
|
read_unlock(&fp->rq_list_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sg_proc_seq_show_debug(struct seq_file *s, void *v)
|
|
|
|
{
|
|
|
|
struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
|
|
|
|
Sg_device *sdp;
|
2009-01-22 02:45:50 +07:00
|
|
|
unsigned long iflags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-06-25 19:08:03 +07:00
|
|
|
if (it && (0 == it->index))
|
|
|
|
seq_printf(s, "max_active_device=%d def_reserved_size=%d\n",
|
|
|
|
(int)it->max, sg_big_buff);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-22 02:45:50 +07:00
|
|
|
read_lock_irqsave(&sg_index_lock, iflags);
|
|
|
|
sdp = it ? sg_lookup_dev(it->index) : NULL;
|
2014-06-25 19:08:03 +07:00
|
|
|
if (NULL == sdp)
|
|
|
|
goto skip;
|
|
|
|
read_lock(&sdp->sfd_lock);
|
|
|
|
if (!list_empty(&sdp->sfds)) {
|
2013-10-25 16:21:57 +07:00
|
|
|
seq_printf(s, " >>> device=%s ", sdp->disk->disk_name);
|
2014-06-25 19:08:03 +07:00
|
|
|
if (atomic_read(&sdp->detaching))
|
|
|
|
seq_puts(s, "detaching pending close ");
|
|
|
|
else if (sdp->device) {
|
|
|
|
struct scsi_device *scsidp = sdp->device;
|
|
|
|
|
2014-06-25 20:27:36 +07:00
|
|
|
seq_printf(s, "%d:%d:%d:%llu em=%d",
|
2014-06-25 19:08:03 +07:00
|
|
|
scsidp->host->host_no,
|
|
|
|
scsidp->channel, scsidp->id,
|
|
|
|
scsidp->lun,
|
|
|
|
scsidp->host->hostt->emulated);
|
|
|
|
}
|
|
|
|
seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n",
|
|
|
|
sdp->sg_tablesize, sdp->exclude, sdp->open_cnt);
|
2013-10-25 16:21:57 +07:00
|
|
|
sg_proc_debug_helper(s, sdp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-06-25 19:08:03 +07:00
|
|
|
read_unlock(&sdp->sfd_lock);
|
|
|
|
skip:
|
2009-01-22 02:45:50 +07:00
|
|
|
read_unlock_irqrestore(&sg_index_lock, iflags);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_SCSI_PROC_FS */
|
|
|
|
|
|
|
|
module_init(init_sg);
|
|
|
|
module_exit(exit_sg);
|