patch-2.3.43 linux/drivers/scsi/sg.c
Next file: linux/drivers/scsi/sr.c
Previous file: linux/drivers/scsi/sd.c
Back to the patch index
Back to the overall index
- Lines: 2784
- Date:
Thu Feb 10 12:37:22 2000
- Orig file:
v2.3.42/linux/drivers/scsi/sg.c
- Orig date:
Fri Jan 7 19:13:22 2000
diff -u --recursive --new-file v2.3.42/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
@@ -6,7 +6,7 @@
*
* Original driver (sg.c):
* Copyright (C) 1992 Lawrence Foard
- * 2.x extensions to driver:
+ * Version 2 and 3 extensions to driver:
* Copyright (C) 1998, 1999 Douglas Gilbert
*
* This program is free software; you can redistribute it and/or modify
@@ -14,35 +14,29 @@
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
- * Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
*/
- static char * sg_version_str = "Version: 2.3.35 (990708)";
- static int sg_version_num = 20335; /* 2 digits for each component */
+ static char * sg_version_str = "Version: 3.1.10 (20000123)";
+ static int sg_version_num = 30110; /* 2 digits for each component */
/*
* D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
* - 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).
- * Then before running the program to be debugged enter:
- * # echo "scsi log timeout 7" > /proc/scsi/scsi
+ * (otherwise the macros compile to empty statements).
+ * Then before running the program to be debugged enter:
+ * # echo "scsi log timeout 7" > /proc/scsi/scsi
* This will send copious output to the console and the log which
* is usually /var/log/messages. To turn off debugging enter:
- * # echo "scsi log timeout 0" > /proc/scsi/scsi
+ * # echo "scsi log timeout 0" > /proc/scsi/scsi
* The 'timeout' token was chosen because it is relatively unused.
* The token 'hlcomplete' should be used but that triggers too
* much output from the sd device driver. To dump the current
* state of the SCSI mid level data structures enter:
- * # echo "scsi dump 1" > /proc/scsi/scsi
- * To dump the state of sg's data structures get the 'sg_debug'
- * program from the utilities and enter:
- * # sg_debug /dev/sga
- * or any valid sg device name. The state of _all_ sg devices
- * will be sent to the console and the log.
- *
- * - The 'alt_address' field in the scatter_list structure and the
- * related 'mem_src' indicate the source of the heap allocation.
+ * # echo "scsi dump 1" > /proc/scsi/scsi
+ * To dump the state of sg's data structures use:
+ * # cat /proc/scsi/sg/debug
*
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -54,6 +48,7 @@
#include <linux/mtio.h>
#include <linux/ioctl.h>
#include <linux/fcntl.h>
+#include <linux/init.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -65,13 +60,29 @@
#include <scsi/scsi_ioctl.h>
#include <scsi/sg.h>
-static spinlock_t sg_request_lock = SPIN_LOCK_UNLOCKED;
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+static int sg_proc_init(void);
+static void sg_proc_cleanup(void);
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+
+/* #define SG_ALLOW_DIO */
+#ifdef SG_ALLOW_DIO
+#include <linux/iobuf.h>
+#endif
-int sg_big_buff = SG_DEF_RESERVED_SIZE; /* sg_big_buff is ro through sysctl */
-/* N.B. This global is here to keep existing software happy. It now holds
- the size of the reserve buffer of the most recent sucessful sg_open().
- Only available when 'sg' compiled into kernel (rather than a module).
- This is deprecated (use SG_GET_RESERVED_SIZE ioctl() instead). */
+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 */
#define SG_SECTOR_SZ 512
#define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
@@ -81,17 +92,10 @@
static int sg_pool_secs_avail = SG_MAX_POOL_SECTORS;
-/* #define SG_DEBUG */ /* for counting varieties of allocations */
-
-#ifdef SG_DEBUG
-static int sg_num_kmal = 0;
-static int sg_num_pool = 0;
-static int sg_num_page = 0;
-#endif
-
#define SG_HEAP_PAGE 1 /* heap from kernel via get_free_pages() */
#define SG_HEAP_KMAL 2 /* heap from kernel via kmalloc() */
#define SG_HEAP_POOL 3 /* heap from scsi dma pool (mid-level) */
+#define SG_USER_MEM 4 /* memory belongs to user space */
static int sg_init(void);
@@ -100,28 +104,36 @@
static int sg_detect(Scsi_Device *);
static void sg_detach(Scsi_Device *);
+static Scsi_Cmnd * dummy_cmdp = 0; /* only used for sizeof */
+
-struct Scsi_Device_Template sg_template =
+static spinlock_t sg_request_lock = SPIN_LOCK_UNLOCKED;
+
+struct Scsi_Device_Template sg_template =
{
- tag:"sg",
- scsi_type:0xff,
- major:SCSI_GENERIC_MAJOR,
- detect:sg_detect,
- init:sg_init,
- finish:sg_finish,
- attach:sg_attach,
- detach:sg_detach
+ tag:"sg",
+ scsi_type:0xff,
+ major:SCSI_GENERIC_MAJOR,
+ detect:sg_detect,
+ init:sg_init,
+ finish:sg_finish,
+ attach:sg_attach,
+ detach:sg_detach
};
+/* Need to add 'rwlock_t sg_rw_lock = RW_LOCK_UNLOCKED;' for list protection */
typedef struct sg_scatter_hold /* holding area for scsi scatter gather info */
{
- unsigned short use_sg; /* Number of pieces of scatter-gather */
+ unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */
unsigned short sglist_len; /* size of malloc'd scatter-gather list */
unsigned bufflen; /* Size of (aggregate) data buffer */
unsigned b_malloc_len; /* actual len malloc'ed in buffer */
void * buffer; /* Data buffer or scatter list,12 bytes each*/
- char mem_src; /* heap whereabouts of 'buffer' */
+ struct kiobuf * kiobp; /* for direct IO information */
+ char mapped; /* indicates kiobp has locked pages */
+ char buffer_mem_src; /* heap whereabouts of 'buffer' */
+ unsigned char cmd_opcode; /* first byte of command */
} Sg_scatter_hold; /* 20 bytes long on i386 */
struct sg_device; /* forward declarations */
@@ -129,20 +141,23 @@
typedef struct sg_request /* SG_MAX_QUEUE requests outstanding per file */
{
- Scsi_Cmnd * my_cmdp; /* NULL -> ready to read, else id */
+ Scsi_Cmnd * my_cmdp; /* != 0 when request with lower levels */
struct sg_request * nextrp; /* NULL -> tail request (slist) */
struct sg_fd * parentfp; /* NULL -> not in use */
Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
- struct sg_header header; /* scsi command+info, see <scsi/sg.h> */
+ sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */
+ unsigned char sense_b[sizeof(dummy_cmdp->sense_buffer)];
char res_used; /* 1 -> using reserve buffer, 0 -> not ... */
-} Sg_request; /* 72 bytes long on i386 */
+ char orphan; /* 1 -> drop on sight, 0 -> normal */
+ char sg_io_owned; /* 1 -> packet belongs to SG_IO */
+ char done; /* 1 -> bh handler done, 0 -> prior to bh */
+} Sg_request; /* 168 bytes long on i386 */
typedef struct sg_fd /* holds the state of a file descriptor */
{
struct sg_fd * nextfp; /* NULL when last opened fd on this device */
struct sg_device * parentdp; /* owning device */
wait_queue_head_t read_wait; /* queue read until command done */
- wait_queue_head_t write_wait; /* write waits on pending read */
int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
Sg_scatter_hold reserve; /* buffer held for this file descriptor */
unsigned save_scat_len; /* original length of trunc. scat. element */
@@ -152,60 +167,73 @@
char low_dma; /* as in parent but possibly overridden to 1 */
char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */
char closed; /* 1 -> fd closed but request(s) outstanding */
- char my_mem_src; /* heap whereabouts of this Sg_fd object */
+ char fd_mem_src; /* heap whereabouts of this Sg_fd object */
char cmd_q; /* 1 -> allow command queuing, 0 -> don't */
- char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */
+ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */
} Sg_fd; /* 1212 bytes long on i386 */
typedef struct sg_device /* holds the state of each scsi generic device */
{
Scsi_Device * device;
- wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
+ wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
int sg_tablesize; /* adapter's max scatter-gather table size */
Sg_fd * headfp; /* first open fd belonging to this device */
kdev_t i_rdev; /* holds device major+minor number */
char exclude; /* opened for exclusive access */
char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
- unsigned char merge_fd; /* 0->sequencing per fd, else fd count */
} Sg_device; /* 24 bytes long on i386 */
static int sg_fasync(int fd, struct file * filp, int mode);
-static void sg_command_done(Scsi_Cmnd * SCpnt);
-static int sg_start_req(Sg_request * srp, int max_buff_size,
- const char * inp, int num_write_xfer);
-static void sg_finish_rem_req(Sg_request * srp, char * outp,
- int num_read_xfer);
-static int sg_build_scat(Sg_scatter_hold * schp, int buff_size,
- const Sg_fd * sfp);
-static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp,
- int num_write_xfer);
+static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt);
+static int sg_start_req(Sg_request * srp);
+static void sg_finish_rem_req(Sg_request * srp);
+static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
+static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp);
+static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count,
+ Sg_request * srp);
+static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count,
+ int blocking, int read_only, Sg_request ** o_srp);
+static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
+ unsigned char * cmnd, int timeout, int blocking);
+static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
+ int wr_xf, int * countp, unsigned char ** up);
+static int sg_write_xfer(Sg_request * srp);
+static int sg_read_xfer(Sg_request * srp);
+static void sg_read_oxfer(Sg_request * srp, char * outp, int num_read_xfer);
static void sg_remove_scat(Sg_scatter_hold * schp);
-static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
- int num_read_xfer);
+static char * sg_get_sgat_msa(Sg_scatter_hold * schp);
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);
-static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp,
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp,
int * mem_srcp);
static void sg_free(char * buff, int size, int mem_src);
-static char * sg_low_malloc(int rqSz, int lowDma, int mem_src,
+static char * sg_low_malloc(int rqSz, int lowDma, int mem_src,
int * retSzp);
static void sg_low_free(char * buff, int size, int mem_src);
-static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev, int get_reserved);
+static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev);
static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id);
static Sg_request * sg_add_request(Sg_fd * sfp);
-static int sg_remove_request(Sg_fd * sfp, const Sg_request * srp);
+static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
static int sg_res_in_use(const Sg_fd * sfp);
+static int sg_dio_in_use(const Sg_fd * sfp);
static void sg_clr_scpnt(Scsi_Cmnd * SCpnt);
static void sg_shorten_timeout(Scsi_Cmnd * scpnt);
-static void sg_debug(const Sg_device * sdp, const Sg_fd * sfp, int part_of);
-static void sg_debug_all(const Sg_fd * sfp);
+static int sg_ms_to_jif(unsigned int msecs);
+static unsigned sg_jif_to_ms(int jifs);
+static int sg_allow_access(unsigned char opcode, char dev_type);
+static int sg_last_dev(void);
+static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
+static void sg_unmap_and(Sg_scatter_hold * schp, int free_also);
static Sg_device * sg_dev_arr = NULL;
static const int size_sg_header = sizeof(struct sg_header);
+static const int size_sg_io_hdr = sizeof(sg_io_hdr_t);
+static const int size_sg_iovec = sizeof(sg_iovec_t);
+static const int size_sg_req_info = sizeof(sg_req_info_t);
static int sg_open(struct inode * inode, struct file * filp)
@@ -240,8 +268,8 @@
if (sdp->headfp && (filp->f_flags & O_NONBLOCK))
return -EBUSY;
res = 0; /* following is a macro that beats race condition */
- __wait_event_interruptible(sdp->o_excl_wait,
- ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)),
+ __wait_event_interruptible(sdp->o_excl_wait,
+ ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)),
res);
if (res)
return res; /* -ERESTARTSYS because signal hit process */
@@ -257,15 +285,9 @@
if (! sdp->headfp) { /* no existing opens on this device */
sdp->sgdebug = 0;
sdp->sg_tablesize = sdp->device->host->sg_tablesize;
- sdp->merge_fd = 0; /* A little tricky if SG_DEF_MERGE_FD set */
}
- if ((sfp = sg_add_sfp(sdp, dev, O_RDWR == (flags & O_ACCMODE)))) {
+ if ((sfp = sg_add_sfp(sdp, dev)))
filp->private_data = sfp;
-#if SG_DEF_MERGE_FD
- if (0 == sdp->merge_fd)
- sdp->merge_fd = 1;
-#endif
- }
else {
if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */
return -ENOMEM;
@@ -287,12 +309,10 @@
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", MINOR(sdp->i_rdev)));
- sg_fasync(-1, filp, 0); /* remove filp from async notification list */
+ sg_fasync(-1, filp, 0); /* remove filp from async notification list */
sg_remove_sfp(sdp, sfp);
- if (! sdp->headfp) {
+ if (! sdp->headfp)
filp->private_data = NULL;
- sdp->merge_fd = 0;
- }
if (sdp->device->host->hostt->module)
__MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
@@ -311,70 +331,162 @@
Sg_fd * sfp;
Sg_request * srp;
int req_pack_id = -1;
- struct sg_header * shp = (struct sg_header *)buf;
+ struct sg_header old_hdr;
+ sg_io_hdr_t new_hdr;
+ sg_io_hdr_t * hp;
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
- SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n",
+ SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n",
MINOR(sdp->i_rdev), (int)count));
-
+
if(! scsi_block_when_processing_errors(sdp->device))
return -ENXIO;
if (ppos != &filp->f_pos)
; /* FIXME: Hmm. Seek to the right place, or fail? */
if ((k = verify_area(VERIFY_WRITE, buf, count)))
return k;
- if (sfp->force_packid && (count >= size_sg_header))
- req_pack_id = shp->pack_id;
+ if (sfp->force_packid && (count >= size_sg_header)) {
+ __copy_from_user(&old_hdr, buf, size_sg_header);
+ if (old_hdr.reply_len < 0) {
+ if (count >= size_sg_io_hdr) {
+ __copy_from_user(&new_hdr, buf, size_sg_io_hdr);
+ req_pack_id = new_hdr.pack_id;
+ }
+ }
+ else
+ req_pack_id = old_hdr.pack_id;
+ }
srp = sg_get_request(sfp, req_pack_id);
if (! srp) { /* now wait on packet to arrive */
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- res = 0; /* following is a macro that beats race condition */
- __wait_event_interruptible(sfp->read_wait,
+ while (1) {
+ int dio = sg_dio_in_use(sfp);
+ res = 0; /* following is a macro that beats race condition */
+ __wait_event_interruptible(sfp->read_wait,
(srp = sg_get_request(sfp, req_pack_id)),
res);
- if (res)
- return res; /* -ERESTARTSYS because signal hit process */
+ if (0 == res)
+ break;
+ else if (! dio) /* only let signal out if no dio */
+ return res; /* -ERESTARTSYS because signal hit process */
+ }
+ }
+ if (srp->header.interface_id != '\0')
+ return sg_new_read(sfp, buf, count, srp);
+
+ hp = &srp->header;
+ memset(&old_hdr, 0, size_sg_header);
+ old_hdr.reply_len = (int)hp->timeout;
+ old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */
+ old_hdr.pack_id = hp->pack_id;
+ old_hdr.twelve_byte =
+ ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0;
+ old_hdr.target_status = hp->masked_status;
+ old_hdr.host_status = hp->host_status;
+ old_hdr.driver_status = hp->driver_status;
+ if ((CHECK_CONDITION & hp->masked_status) ||
+ (DRIVER_SENSE & hp->driver_status))
+ memcpy(old_hdr.sense_buffer, srp->sense_b,
+ sizeof(old_hdr.sense_buffer));
+ 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:
+ old_hdr.result = 0;
+ break;
+ case DID_NO_CONNECT:
+ case DID_BUS_BUSY:
+ case DID_TIME_OUT:
+ old_hdr.result = EBUSY;
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ case DID_PARITY:
+ case DID_RESET:
+ case DID_BAD_INTR:
+ old_hdr.result = EIO;
+ break;
+ case DID_ERROR:
+ old_hdr.result =
+ (srp->sense_b[0] == 0 && hp->masked_status == GOOD) ? 0 : EIO;
+ break;
+ default:
+ old_hdr.result = EIO;
+ break;
}
- if (2 != sfp->underrun_flag)
- srp->header.pack_len = srp->header.reply_len; /* Why ????? */
/* Now copy the result back to the user buffer. */
if (count >= size_sg_header) {
- __copy_to_user(buf, &srp->header, size_sg_header);
+ __copy_to_user(buf, &old_hdr, size_sg_header);
buf += size_sg_header;
- if (count > srp->header.reply_len)
- count = srp->header.reply_len;
- if (count > size_sg_header) /* release does copy_to_user */
- sg_finish_rem_req(srp, buf, count - size_sg_header);
- else
- sg_finish_rem_req(srp, NULL, 0);
+ if (count > old_hdr.reply_len)
+ count = old_hdr.reply_len;
+ if (count > size_sg_header)
+ sg_read_oxfer(srp, buf, count - size_sg_header);
}
- else {
- count = (srp->header.result == 0) ? 0 : -EIO;
- sg_finish_rem_req(srp, NULL, 0);
- }
- if (! sfp->cmd_q)
- wake_up_interruptible(&sfp->write_wait);
+ else
+ count = (old_hdr.result == 0) ? 0 : -EIO;
+ sg_finish_rem_req(srp);
return count;
}
-static ssize_t sg_write(struct file * filp, const char * buf,
+static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count,
+ Sg_request * srp)
+{
+ Sg_device * sdp = sfp->parentdp;
+ sg_io_hdr_t * hp = &srp->header;
+ int k, len;
+
+ if(! scsi_block_when_processing_errors(sdp->device) )
+ return -ENXIO;
+ if (count < size_sg_io_hdr)
+ return -EINVAL;
+
+ hp->sb_len_wr = 0;
+ if ((hp->mx_sb_len > 0) && hp->sbp) {
+ if ((CHECK_CONDITION & hp->masked_status) ||
+ (DRIVER_SENSE & hp->driver_status)) {
+ int sb_len = sizeof(dummy_cmdp->sense_buffer);
+ 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 ((k = verify_area(VERIFY_WRITE, hp->sbp, len)))
+ return k;
+ __copy_to_user(hp->sbp, srp->sense_b, len);
+ hp->sb_len_wr = len;
+ }
+ }
+ if (hp->masked_status || hp->host_status || hp->driver_status)
+ hp->info |= SG_INFO_CHECK;
+ copy_to_user(buf, hp, size_sg_io_hdr);
+
+ k = sg_read_xfer(srp);
+ if (k) return k; /* probably -EFAULT, bad addr in dxferp or iovec list */
+ sg_finish_rem_req(srp);
+ return count;
+}
+
+
+static ssize_t sg_write(struct file * filp, const char * buf,
size_t count, loff_t *ppos)
{
int mxsize, cmd_size, k;
- unsigned char cmnd[MAX_COMMAND_SIZE];
- int input_size;
+ int input_size, blocking;
unsigned char opcode;
- Scsi_Cmnd * SCpnt;
Sg_device * sdp;
Sg_fd * sfp;
Sg_request * srp;
+ struct sg_header old_hdr;
+ sg_io_hdr_t * hp;
+ unsigned char cmnd[sizeof(dummy_cmdp->cmnd)];
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
- SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n",
+ SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n",
MINOR(sdp->i_rdev), (int)count));
if(! scsi_block_when_processing_errors(sdp->device) )
@@ -384,33 +496,26 @@
if ((k = verify_area(VERIFY_READ, buf, count)))
return k; /* protects following copy_from_user()s + get_user()s */
+ if (count < size_sg_header)
+ return -EIO;
+ __copy_from_user(&old_hdr, buf, size_sg_header);
+ blocking = !(filp->f_flags & O_NONBLOCK);
+ if (old_hdr.reply_len < 0)
+ return sg_new_write(sfp, buf, count, blocking, 0, NULL);
if (count < (size_sg_header + 6))
- return -EIO; /* The minimum scsi command length is 6 bytes. */
+ return -EIO; /* The minimum scsi command length is 6 bytes. */
if (! (srp = sg_add_request(sfp))) {
- if (sfp->cmd_q) {
- SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
- return -EDOM;
- }
- else { /* old semantics: wait for pending read() to finish */
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- k = 0;
- __wait_event_interruptible(sfp->write_wait,
- (srp = sg_add_request(sfp)),
- k);
- if (k)
- return k; /* -ERESTARTSYS because signal hit process */
- }
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
+ return -EDOM;
}
- __copy_from_user(&srp->header, buf, size_sg_header);
buf += size_sg_header;
- srp->header.pack_len = count;
__get_user(opcode, buf);
if (sfp->next_cmd_len > 0) {
if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
sfp->next_cmd_len = 0;
+ sg_remove_request(sfp, srp);
return -EIO;
}
cmd_size = sfp->next_cmd_len;
@@ -418,87 +523,204 @@
}
else {
cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */
- if ((opcode >= 0xc0) && srp->header.twelve_byte)
+ if ((opcode >= 0xc0) && old_hdr.twelve_byte)
cmd_size = 12;
}
- SCSI_LOG_TIMEOUT(4, printk("sg_write: scsi opcode=0x%02x, cmd_size=%d\n",
+ SCSI_LOG_TIMEOUT(4, printk("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 > srp->header.reply_len) ? input_size :
- srp->header.reply_len;
+ mxsize = (input_size > old_hdr.reply_len) ? input_size :
+ old_hdr.reply_len;
mxsize -= size_sg_header;
input_size -= size_sg_header;
if (input_size < 0) {
sg_remove_request(sfp, srp);
return -EIO; /* User did not pass enough bytes for this command. */
}
- if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) {
- SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k));
- sg_finish_rem_req(srp, NULL, 0);
+ 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 - size_sg_header) > 0) ?
+ 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;
+ hp->dxferp = (unsigned char *)buf + cmd_size;
+ 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;
+ __copy_from_user(cmnd, buf, cmd_size);
+ k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
+ return (k < 0) ? k : count;
+}
+
+static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count,
+ int blocking, int read_only, Sg_request ** o_srp)
+{
+ int k;
+ Sg_request * srp;
+ sg_io_hdr_t * hp;
+ unsigned char cmnd[sizeof(dummy_cmdp->cmnd)];
+ int timeout;
+
+ if (count < size_sg_io_hdr)
+ return -EINVAL;
+ if ((k = verify_area(VERIFY_READ, buf, count)))
+ return k; /* protects following copy_from_user()s + get_user()s */
+
+ sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */
+ if (! (srp = sg_add_request(sfp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n"));
+ return -EDOM;
+ }
+ hp = &srp->header;
+ __copy_from_user(hp, buf, size_sg_io_hdr);
+ if (hp->interface_id != 'S') {
+ sg_remove_request(sfp, srp);
+ return -ENOSYS;
+ }
+ timeout = sg_ms_to_jif(srp->header.timeout);
+ if ((! hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof(cmnd))) {
+ sg_remove_request(sfp, srp);
+ return -EMSGSIZE;
+ }
+ if ((k = verify_area(VERIFY_READ, hp->cmdp, hp->cmd_len))) {
+ sg_remove_request(sfp, srp);
+ return k; /* protects following copy_from_user()s + get_user()s */
+ }
+ __copy_from_user(cmnd, hp->cmdp, hp->cmd_len);
+ if (read_only &&
+ (! sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {
+ sg_remove_request(sfp, srp);
+ return -EACCES;
+ }
+ 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)
+{
+ int k;
+ Scsi_Cmnd * SCpnt;
+ 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;
+ SCSI_LOG_TIMEOUT(4,
+ printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n",
+ (int)cmnd[0], (int)hp->cmd_len));
+
+ if ((k = sg_start_req(srp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: start_req err=%d\n", k));
+ sg_finish_rem_req(srp);
return k; /* probably out of space --> ENOMEM */
}
+ if ((k = sg_write_xfer(srp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: write_xfer, bad address\n"));
+ sg_finish_rem_req(srp);
+ return k;
+ }
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
- if (! (SCpnt = scsi_allocate_device(sdp->device,
- !(filp->f_flags & O_NONBLOCK),
- TRUE))) {
- sg_finish_rem_req(srp, NULL, 0);
- if( signal_pending(current) )
- {
- return -EINTR;
- }
- return -EAGAIN; /* No available command blocks at the moment */
+ SCpnt = scsi_allocate_device(sdp->device, blocking, TRUE);
+ if (! SCpnt) {
+ sg_finish_rem_req(srp);
+ return (signal_pending(current)) ? -EINTR : -EAGAIN;
+ /* No available command blocks, or, interrupted while waiting */
}
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */
srp->my_cmdp = SCpnt;
SCpnt->request.rq_dev = sdp->i_rdev;
SCpnt->request.rq_status = RQ_ACTIVE;
SCpnt->sense_buffer[0] = 0;
- SCpnt->cmd_len = cmd_size;
- __copy_from_user(cmnd, buf, cmd_size);
+ SCpnt->cmd_len = hp->cmd_len;
/* Set the LUN field in the command structure, overriding user input */
- cmnd[1]= (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
+ if (! (hp->flags & SG_FLAG_LUN_INHIBIT))
+ cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */
- SCpnt->use_sg = srp->data.use_sg;
+ SCpnt->use_sg = srp->data.k_use_sg;
SCpnt->sglist_len = srp->data.sglist_len;
SCpnt->bufflen = srp->data.bufflen;
- if (1 == sfp->underrun_flag)
- SCpnt->underflow = srp->data.bufflen;
- else
- SCpnt->underflow = 0;
+ SCpnt->underflow = 0;
SCpnt->buffer = srp->data.buffer;
- srp->data.use_sg = 0;
+ srp->data.k_use_sg = 0;
srp->data.sglist_len = 0;
srp->data.bufflen = 0;
srp->data.buffer = NULL;
+ hp->duration = jiffies;
/* Now send everything of to mid-level. The next time we hear about this
- packet is when sg_command_done() is called (ie a callback). */
+ packet is when sg_cmd_done_bh() is called (i.e. a callback). */
scsi_do_cmd(SCpnt, (void *)cmnd,
- (void *)SCpnt->buffer, mxsize,
- sg_command_done, sfp->timeout, SG_DEFAULT_RETRIES);
- /* 'mxsize' overwrites SCpnt->bufflen, hence need for b_malloc_len */
-/* SCSI_LOG_TIMEOUT(6, printk("sg_write: sent scsi cmd to mid-level\n")); */
- return count;
+ (void *)SCpnt->buffer, hp->dxfer_len,
+ sg_cmd_done_bh, timeout, SG_DEFAULT_RETRIES);
+ /* dxfer_len overwrites SCpnt->bufflen, hence need for b_malloc_len */
+ return 0;
}
static int sg_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd_in, unsigned long arg)
{
- int result, val;
+ int result, val, read_only;
Sg_device * sdp;
Sg_fd * sfp;
Sg_request * srp;
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
- SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n",
+ SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n",
MINOR(sdp->i_rdev), (int)cmd_in));
if(! scsi_block_when_processing_errors(sdp->device) )
return -ENXIO;
+ read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
switch(cmd_in)
{
+ case SG_IO:
+ {
+ int blocking = 1; /* ignore O_NONBLOCK flag */
+
+ if(! scsi_block_when_processing_errors(sdp->device) )
+ return -ENXIO;
+ result = verify_area(VERIFY_WRITE, (void *)arg, size_sg_io_hdr);
+ if (result) return result;
+ result = sg_new_write(sfp, (const char *)arg, size_sg_io_hdr,
+ blocking, read_only, &srp);
+ if (result < 0) return result;
+ srp->sg_io_owned = 1;
+ while (1) {
+ int dio = sg_dio_in_use(sfp);
+ result = 0; /* following macro to beat race condition */
+ __wait_event_interruptible(sfp->read_wait,
+ (sfp->closed || srp->done), result);
+ if (sfp->closed)
+ return 0; /* request packet dropped already */
+ if (0 == result)
+ break;
+ else if (! dio) { /* only let signal out if no dio */
+ srp->orphan = 1;
+ return result; /* -ERESTARTSYS because signal hit process */
+ }
+ }
+ result = sg_new_read(sfp, (char *)arg, size_sg_io_hdr, srp);
+ return (result < 0) ? result : 0;
+ }
case SG_SET_TIMEOUT:
result = get_user(val, (int *)arg);
if (result) return result;
@@ -525,21 +747,21 @@
case SG_GET_LOW_DMA:
return put_user((int)sfp->low_dma, (int *)arg);
case SG_GET_SCSI_ID:
- result = verify_area(VERIFY_WRITE, (void *)arg, sizeof(Sg_scsi_id));
+ result = verify_area(VERIFY_WRITE, (void *)arg, sizeof(sg_scsi_id_t));
if (result) return result;
else {
- Sg_scsi_id * sg_idp = (Sg_scsi_id *)arg;
+ sg_scsi_id_t * sg_idp = (sg_scsi_id_t *)arg;
__put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
__put_user((int)sdp->device->channel, &sg_idp->channel);
__put_user((int)sdp->device->id, &sg_idp->scsi_id);
__put_user((int)sdp->device->lun, &sg_idp->lun);
__put_user((int)sdp->device->type, &sg_idp->scsi_type);
- __put_user((short)sdp->device->host->cmd_per_lun,
+ __put_user((short)sdp->device->host->cmd_per_lun,
&sg_idp->h_cmd_per_lun);
- __put_user((short)sdp->device->queue_depth,
+ __put_user((short)sdp->device->queue_depth,
&sg_idp->d_queue_depth);
- __put_user(0, &sg_idp->unused1);
- __put_user(0, &sg_idp->unused2);
+ __put_user(0, &sg_idp->unused[0]);
+ __put_user(0, &sg_idp->unused[1]);
return 0;
}
case SG_SET_FORCE_PACK_ID:
@@ -552,7 +774,7 @@
if (result) return result;
srp = sfp->headrp;
while (srp) {
- if (! srp->my_cmdp) {
+ if (srp->done && (! srp->sg_io_owned)) {
__put_user(srp->header.pack_id, (int *)arg);
return 0;
}
@@ -564,7 +786,7 @@
srp = sfp->headrp;
val = 0;
while (srp) {
- if (! srp->my_cmdp)
+ if (srp->done && (! srp->sg_io_owned))
++val;
srp = srp->nextrp;
}
@@ -572,8 +794,6 @@
case SG_GET_SG_TABLESIZE:
return put_user(sdp->sg_tablesize, (int *)arg);
case SG_SET_RESERVED_SIZE:
- if (O_RDWR != (filp->f_flags & O_ACCMODE))
- return -EACCES;
result = get_user(val, (int *)arg);
if (result) return result;
if (val != sfp->reserve.bufflen) {
@@ -586,20 +806,6 @@
case SG_GET_RESERVED_SIZE:
val = (int)sfp->reserve.bufflen;
return put_user(val, (int *)arg);
- case SG_GET_MERGE_FD:
- return put_user((int)sdp->merge_fd, (int *)arg);
- case SG_SET_MERGE_FD:
- if (O_RDWR != (filp->f_flags & O_ACCMODE))
- return -EACCES; /* require write access since effect wider
- then just this fd */
- result = get_user(val, (int *)arg);
- if (result) return result;
- val = val ? 1 : 0;
- if ((val ^ (0 != sdp->merge_fd)) &&
- sdp->headfp && sdp->headfp->nextfp)
- return -EBUSY; /* too much work if multiple fds already */
- sdp->merge_fd = val;
- return 0;
case SG_SET_COMMAND_Q:
result = get_user(val, (int *)arg);
if (result) return result;
@@ -607,13 +813,13 @@
return 0;
case SG_GET_COMMAND_Q:
return put_user((int)sfp->cmd_q, (int *)arg);
- case SG_SET_UNDERRUN_FLAG:
+ case SG_SET_KEEP_ORPHAN:
result = get_user(val, (int *)arg);
if (result) return result;
- sfp->underrun_flag = val;
+ sfp->keep_orphan = val;
return 0;
- case SG_GET_UNDERRUN_FLAG:
- return put_user((int)sfp->underrun_flag, (int *)arg);
+ case SG_GET_KEEP_ORPHAN:
+ return put_user((int)sfp->keep_orphan, (int *)arg);
case SG_NEXT_CMD_LEN:
result = get_user(val, (int *)arg);
if (result) return result;
@@ -621,6 +827,32 @@
return 0;
case SG_GET_VERSION_NUM:
return put_user(sg_version_num, (int *)arg);
+ case SG_GET_REQUEST_TABLE:
+ result = verify_area(VERIFY_WRITE, (void *) arg,
+ size_sg_req_info * SG_MAX_QUEUE);
+ if (result) return result;
+ else {
+ sg_req_info_t rinfo[SG_MAX_QUEUE];
+ Sg_request * srp = sfp->headrp;
+ for (val = 0; val < SG_MAX_QUEUE;
+ ++val, srp = srp ? srp->nextrp : srp) {
+ memset(&rinfo[val], 0, size_sg_req_info);
+ if (srp) {
+ rinfo[val].req_state = srp->done ? 2 : 1;
+ rinfo[val].problem = srp->header.masked_status &
+ srp->header.host_status & srp->header.driver_status;
+ rinfo[val].duration = srp->done ?
+ sg_jif_to_ms(srp->header.duration) :
+ sg_jif_to_ms(jiffies - srp->header.duration);
+ 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;
+ }
+ }
+ __copy_to_user((void *)arg, rinfo, size_sg_req_info * SG_MAX_QUEUE);
+ return 0;
+ }
case SG_EMULATED_HOST:
return put_user(sdp->device->host->hostt->emulated, (int *)arg);
case SG_SCSI_RESET:
@@ -628,23 +860,22 @@
return -EBUSY;
result = get_user(val, (int *)arg);
if (result) return result;
- /* Don't do anything till scsi mod level visibility */
+ /* Don't do anything till scsi mid level visibility */
return 0;
case SCSI_IOCTL_SEND_COMMAND:
- /* Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the
- user already has read/write access to the generic device and so
- can execute arbitrary SCSI commands. */
- if (O_RDWR != (filp->f_flags & O_ACCMODE))
- return -EACCES; /* very dangerous things can be done here */
+ if (read_only) {
+ unsigned char opcode = WRITE_6;
+ Scsi_Ioctl_Command * siocp = (void *)arg;
+
+ copy_from_user(&opcode, siocp->data, 1);
+ if (! sg_allow_access(opcode, sdp->device->type))
+ return -EACCES;
+ }
return scsi_ioctl_send_command(sdp->device, (void *)arg);
case SG_SET_DEBUG:
result = get_user(val, (int *)arg);
if (result) return result;
sdp->sgdebug = (char)val;
- if (9 == sdp->sgdebug)
- sg_debug(sdp, sfp, 0);
- else if (sdp->sgdebug > 9)
- sg_debug_all(sfp);
return 0;
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
@@ -652,7 +883,7 @@
case SG_GET_TRANSFORM:
return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
default:
- if (O_RDWR != (filp->f_flags & O_ACCMODE))
+ if (read_only)
return -EACCES; /* don't know so take safe approach */
return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
}
@@ -671,18 +902,18 @@
poll_wait(filp, &sfp->read_wait, wait);
srp = sfp->headrp;
while (srp) { /* if any read waiting, flag it */
- if (! (res || srp->my_cmdp))
+ if ((0 == res) && srp->done && (! srp->sg_io_owned))
res = POLLIN | POLLRDNORM;
++count;
srp = srp->nextrp;
}
- if (0 == sfp->cmd_q) {
+ if (! sfp->cmd_q) {
if (0 == count)
res |= POLLOUT | POLLWRNORM;
}
else if (count < SG_MAX_QUEUE)
res |= POLLOUT | POLLWRNORM;
- SCSI_LOG_TIMEOUT(3, printk("sg_poll: dev=%d, res=0x%x\n",
+ SCSI_LOG_TIMEOUT(3, printk("sg_poll: dev=%d, res=0x%x\n",
MINOR(sdp->i_rdev), (int)res));
return res;
}
@@ -695,36 +926,29 @@
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
- SCSI_LOG_TIMEOUT(3, printk("sg_fasync: dev=%d, mode=%d\n",
+ SCSI_LOG_TIMEOUT(3, printk("sg_fasync: dev=%d, mode=%d\n",
MINOR(sdp->i_rdev), mode));
retval = fasync_helper(fd, filp, mode, &sfp->async_qp);
return (retval < 0) ? retval : 0;
}
-/* This function is called by the interrupt handler when we
- * actually have a command that is complete. */
-static void sg_command_done(Scsi_Cmnd * SCpnt)
+/* This function is a "bottom half" handler that is called by the
+ * mid level when a command is completed (or has failed). */
+static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
{
int dev = MINOR(SCpnt->request.rq_dev);
Sg_device * sdp;
Sg_fd * sfp;
Sg_request * srp = NULL;
- int closed = 0;
- static const int min_sb_len =
- SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ?
- sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE;
- if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)) {
- SCSI_LOG_TIMEOUT(1, printk("sg__done: bad args dev=%d\n", dev));
+ if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)
+ || (NULL == (sdp = &sg_dev_arr[dev]))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg...bh: bad args dev=%d\n", dev));
scsi_release_command(SCpnt);
SCpnt = NULL;
return;
}
- sdp = &sg_dev_arr[dev];
- if (NULL == sdp->device)
- return; /* Get out of here quick ... */
-
sfp = sdp->headfp;
while (sfp) {
srp = sfp->headrp;
@@ -738,215 +962,88 @@
sfp = sfp->nextfp;
}
if (! srp) {
- SCSI_LOG_TIMEOUT(1, printk("sg__done: req missing, dev=%d\n", dev));
+ SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev));
scsi_release_command(SCpnt);
SCpnt = NULL;
return;
}
-/* First transfer ownership of data buffers to sg_device object. */
- srp->data.use_sg = SCpnt->use_sg;
+ /* First transfer ownership of data buffers to sg_device object. */
+ srp->data.k_use_sg = SCpnt->use_sg;
srp->data.sglist_len = SCpnt->sglist_len;
srp->data.bufflen = SCpnt->bufflen;
srp->data.buffer = SCpnt->buffer;
- if (2 == sfp->underrun_flag)
- srp->header.pack_len = SCpnt->underflow;
sg_clr_scpnt(SCpnt);
srp->my_cmdp = NULL;
+ srp->done = 1;
- SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n",
- dev, (int)status_byte(SCpnt->result), (int)SCpnt->result));
- memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, min_sb_len);
- switch (host_byte(SCpnt->result))
- { /* 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:
- srp->header.result = 0;
- break;
- case DID_NO_CONNECT:
- case DID_BUS_BUSY:
- case DID_TIME_OUT:
- srp->header.result = EBUSY;
- break;
- case DID_BAD_TARGET:
- case DID_ABORT:
- case DID_PARITY:
- case DID_RESET:
- case DID_BAD_INTR:
- srp->header.result = EIO;
- break;
- case DID_ERROR:
- if (SCpnt->sense_buffer[0] == 0 &&
- status_byte(SCpnt->result) == GOOD)
- srp->header.result = 0;
- else
- srp->header.result = EIO;
- break;
- default:
- SCSI_LOG_TIMEOUT(1, printk(
- "sg: unexpected host_byte=%d, dev=%d in 'done'\n",
- host_byte(SCpnt->result), dev));
- srp->header.result = EIO;
- break;
- }
-
-/* Following if statement is a patch supplied by Eric Youngdale */
- if (driver_byte(SCpnt->result) != 0
- && (SCpnt->sense_buffer[0] & 0x7f) == 0x70
- && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION
- && sdp->device->removable) {
-/* Detected disc change. Set the bit - this may be used if there are */
-/* filesystems using this device. */
- sdp->device->changed = 1;
- }
- srp->header.target_status = status_byte(SCpnt->result);
- if ((sdp->sgdebug > 0) &&
- ((CHECK_CONDITION == srp->header.target_status) ||
- (COMMAND_TERMINATED == srp->header.target_status)))
- print_sense("sg_command_done", SCpnt);
- srp->header.host_status = host_byte(SCpnt->result);
- srp->header.driver_status = driver_byte(SCpnt->result);
+ SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n",
+ dev, srp->header.pack_id, (int)SCpnt->result));
+ srp->header.resid = SCpnt->resid;
+ /* sg_unmap_and(&srp->data, 0); */ /* unmap locked pages a.s.a.p. */
+ srp->header.duration = sg_jif_to_ms(jiffies - (int)srp->header.duration);
+ if (0 != SCpnt->result) {
+ memcpy(srp->sense_b, SCpnt->sense_buffer, sizeof(srp->sense_b));
+ srp->header.status = 0xff & SCpnt->result;
+ srp->header.masked_status = status_byte(SCpnt->result);
+ srp->header.msg_status = msg_byte(SCpnt->result);
+ srp->header.host_status = host_byte(SCpnt->result);
+ srp->header.driver_status = driver_byte(SCpnt->result);
+ if ((sdp->sgdebug > 0) &&
+ ((CHECK_CONDITION == srp->header.masked_status) ||
+ (COMMAND_TERMINATED == srp->header.masked_status)))
+ print_sense("sg_cmd_done_bh", SCpnt);
+
+ /* Following if statement is a patch supplied by Eric Youngdale */
+ if (driver_byte(SCpnt->result) != 0
+ && (SCpnt->sense_buffer[0] & 0x7f) == 0x70
+ && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION
+ && sdp->device->removable) {
+ /* Detected disc change. Set the bit - this may be used if */
+ /* there are filesystems using this device. */
+ sdp->device->changed = 1;
+ }
+ }
+ /* Rely on write phase to clean out srp status values, so no "else" */
scsi_release_command(SCpnt);
SCpnt = NULL;
if (sfp->closed) { /* whoops this fd already released, cleanup */
- closed = 1;
SCSI_LOG_TIMEOUT(1,
- printk("sg__done: already closed, freeing ...\n"));
-/* should check if module is unloaded <<<<<<< */
- sg_finish_rem_req(srp, NULL, 0);
- if (NULL == sfp->headrp) {
+ printk("sg...bh: already closed, freeing ...\n"));
+ /* should check if module is unloaded <<<<<<< */
+ sg_finish_rem_req(srp);
+ srp = NULL;
+ if (NULL == sfp->headrp) {
SCSI_LOG_TIMEOUT(1,
- printk("sg__done: already closed, final cleanup\n"));
+ printk("sg...bh: already closed, final cleanup\n"));
sg_remove_sfp(sdp, sfp);
+ sfp = NULL;
}
}
-/* Now wake up any sg_read() that is waiting for this packet. */
- wake_up_interruptible(&sfp->read_wait);
- if ((sfp->async_qp) && (! closed))
- kill_fasync(sfp->async_qp, SIGPOLL, POLL_IN);
-}
-
-static void sg_debug_all(const Sg_fd * sfp)
-{
- const Sg_device * sdp = sg_dev_arr;
- int k;
-
- if (NULL == sg_dev_arr) {
- printk("sg_debug_all: sg_dev_arr NULL, death is imminent\n");
- return;
- }
- if (! sfp)
- printk("sg_debug_all: sfp (file descriptor pointer) NULL\n");
-
- printk("sg_debug_all: dev_max=%d, %s\n",
- sg_template.dev_max, sg_version_str);
- printk(" scsi_dma_free_sectors=%u, sg_pool_secs_aval=%d\n",
- scsi_dma_free_sectors, sg_pool_secs_avail);
- printk(" sg_big_buff=%d\n", sg_big_buff);
-#ifdef SG_DEBUG
- printk(" malloc counts, kmallocs=%d, dma_pool=%d, pages=%d\n",
- sg_num_kmal, sg_num_pool, sg_num_page);
-#endif
- for (k = 0; k < sg_template.dev_max; ++k, ++sdp) {
- if (sdp->headfp) {
- if (! sfp)
- sfp = sdp->headfp; /* just to keep things going */
- else if (sdp == sfp->parentdp)
- printk(" ***** Invoking device follows *****\n");
- sg_debug(sdp, sfp, 1);
- }
- }
-}
-
-static void sg_debug(const Sg_device * sdp, const Sg_fd * sfp, int part_of)
-{
- Sg_fd * fp;
- Sg_request * srp;
- int dev;
- int k;
-
- if (! sfp)
- printk("sg_debug: sfp (file descriptor pointer) NULL\n");
- if (! sdp) {
- printk("sg_debug: sdp pointer (to device) NULL\n");
- return;
- }
- else if (! sdp->device) {
- printk("sg_debug: device detached ??\n");
- return;
- }
- dev = MINOR(sdp->i_rdev);
-
- if (part_of)
- printk(" >>> device=%d(sg%c), ", dev, 'a' + dev);
- else
- printk("sg_debug: device=%d(sg%c), ", dev, 'a' + dev);
- printk("scsi%d chan=%d id=%d lun=%d em=%d\n", sdp->device->host->host_no,
- sdp->device->channel, sdp->device->id, sdp->device->lun,
- sdp->device->host->hostt->emulated);
- printk(" sg_tablesize=%d, excl=%d, sgdebug=%d, merge_fd=%d\n",
- sdp->sg_tablesize, sdp->exclude, sdp->sgdebug, sdp->merge_fd);
- if (! part_of) {
- printk(" scsi_dma_free_sectors=%u, sg_pool_secs_aval=%d\n",
- scsi_dma_free_sectors, sg_pool_secs_avail);
-#ifdef SG_DEBUG
- printk(" mallocs: kmallocs=%d, dma_pool=%d, pages=%d\n",
- sg_num_kmal, sg_num_pool, sg_num_page);
-#endif
- }
-
- fp = sdp->headfp;
- for (k = 1; fp; fp = fp->nextfp, ++k) {
- if (sfp == fp)
- printk(" *** Following data belongs to invoking FD ***\n");
- else if (! fp->parentdp)
- printk(">> Following FD has NULL parent pointer ???\n");
- printk(" FD(%d): timeout=%d, bufflen=%d, use_sg=%d\n",
- k, fp->timeout, fp->reserve.bufflen, (int)fp->reserve.use_sg);
- printk(" low_dma=%d, cmd_q=%d, s_sc_len=%d, f_packid=%d\n",
- (int)fp->low_dma, (int)fp->cmd_q, (int)fp->save_scat_len,
- (int)fp->force_packid);
- printk(" urun_flag=%d, next_cmd_len=%d, closed=%d\n",
- (int)fp->underrun_flag, (int)fp->next_cmd_len,
- (int)fp->closed);
- srp = fp->headrp;
- if (NULL == srp)
- printk(" No requests active\n");
- while (srp) {
- if (srp->res_used)
- printk("reserved buff >> ");
- else
- printk(" ");
- if (srp->my_cmdp)
- printk("written: pack_id=%d, bufflen=%d, use_sg=%d\n",
- srp->header.pack_id, srp->my_cmdp->bufflen,
- srp->my_cmdp->use_sg);
- else
- printk("to_read: pack_id=%d, bufflen=%d, use_sg=%d\n",
- srp->header.pack_id, srp->data.bufflen, srp->data.use_sg);
- if (! srp->parentfp)
- printk(">> request has NULL parent pointer ???\n");
- srp = srp->nextrp;
- }
+ else if (srp && srp->orphan) {
+ if (sfp->keep_orphan)
+ srp->sg_io_owned = 0;
+ else {
+ sg_finish_rem_req(srp);
+ srp = NULL;
+ }
+ }
+ if (sfp && srp) {
+ /* Now wake up any sg_read() that is waiting for this packet. */
+ wake_up_interruptible(&sfp->read_wait);
+ if (sfp->async_qp)
+ kill_fasync(sfp->async_qp, SIGPOLL, POLL_IN);
}
}
static struct file_operations sg_fops = {
- NULL, /* lseek */
- sg_read, /* read */
- sg_write, /* write */
- NULL, /* readdir */
- sg_poll, /* poll */
- sg_ioctl, /* ioctl */
- NULL, /* mmap */
- sg_open, /* open */
- NULL, /* flush */
- sg_release, /* release, was formerly sg_close */
- NULL, /* fsync */
- sg_fasync, /* fasync */
- NULL, /* lock */
+ read: sg_read,
+ write: sg_write,
+ poll: sg_poll,
+ ioctl: sg_ioctl,
+ open: sg_open,
+ release: sg_release,
+ fasync: sg_fasync,
};
@@ -962,7 +1059,7 @@
printk("Detected scsi generic sg%c at scsi%d,"
" channel %d, id %d, lun %d\n",
'a'+sg_template.dev_noticed,
- scsidp->host->host_no, scsidp->channel,
+ scsidp->host->host_no, scsidp->channel,
scsidp->id, scsidp->lun);
}
sg_template.dev_noticed++;
@@ -973,6 +1070,7 @@
static int sg_init()
{
static int sg_registered = 0;
+ int size;
if (sg_template.dev_noticed == 0) return 0;
@@ -990,19 +1088,42 @@
if(sg_dev_arr) return 0;
SCSI_LOG_TIMEOUT(3, printk("sg_init\n"));
- sg_dev_arr = (Sg_device *)
- kmalloc((sg_template.dev_noticed + SG_EXTRA_DEVS)
- * sizeof(Sg_device), GFP_ATOMIC);
- memset(sg_dev_arr, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
- * sizeof(Sg_device));
+ size = sizeof(Sg_device) *
+ (sg_template.dev_noticed + SG_EXTRA_DEVS);
+ sg_dev_arr = (Sg_device *)kmalloc(size, GFP_ATOMIC);
+ memset(sg_dev_arr, 0, size);
if (NULL == sg_dev_arr) {
printk("sg_init: no space for sg_dev_arr\n");
return 1;
}
+#ifdef CONFIG_PROC_FS
+ sg_proc_init();
+#endif /* CONFIG_PROC_FS */
sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
return 0;
}
+#ifndef MODULE
+static int __init sg_def_reserved_size_setup(char *str)
+{
+ int tmp;
+
+ if (get_option(&str, &tmp) == 1) {
+ def_reserved_size = tmp;
+ if (tmp >= 0)
+ sg_big_buff = tmp;
+ return 1;
+ } else {
+ printk("sg_def_reserved_size : usage sg_def_reserved_size=n "
+ "(n could be 65536, 131072 or 262144)\n");
+ return 0;
+ }
+}
+
+__setup("sg_def_reserved_size=", sg_def_reserved_size_setup);
+#endif
+
+
static int sg_attach(Scsi_Device * scsidp)
{
Sg_device * sdp = sg_dev_arr;
@@ -1011,6 +1132,8 @@
if ((sg_template.nr_dev >= sg_template.dev_max) || (! sdp))
{
scsidp->attached--;
+ printk("sg_attach: rejected since exceeds dev_max=%d\n",
+ sg_template.dev_max);
return 1;
}
@@ -1024,7 +1147,6 @@
init_waitqueue_head(&sdp->o_excl_wait);
sdp->headfp= NULL;
sdp->exclude = 0;
- sdp->merge_fd = 0; /* Cope with SG_DEF_MERGE_FD on open */
sdp->sgdebug = 0;
sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0;
sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k);
@@ -1052,13 +1174,13 @@
if(sdp->device != scsidp)
continue; /* dirty but lowers nesting */
if (sdp->headfp) {
-/* Need to stop sg_command_done() playing with this list during this loop */
+/* Need to stop sg_cmd_done_bh() playing with this list during this loop */
spin_lock_irqsave(&sg_request_lock, flags);
sfp = sdp->headfp;
while (sfp) {
srp = sfp->headrp;
while (srp) {
- if (srp->my_cmdp)
+ if (! srp->done)
sg_shorten_timeout(srp->my_cmdp);
srp = srp->nextrp;
}
@@ -1070,7 +1192,7 @@
}
else {
SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k));
- sdp->device = NULL;
+ sdp->device = NULL;
}
scsidp->attached--;
sg_template.nr_dev--;
@@ -1084,7 +1206,12 @@
#ifdef MODULE
+MODULE_PARM(def_reserved_size, "i");
+MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
+
int init_module(void) {
+ if (def_reserved_size >= 0)
+ sg_big_buff = def_reserved_size;
sg_template.module = &__this_module;
return scsi_register_module(MODULE_SCSI_DEV, &sg_template);
}
@@ -1094,10 +1221,13 @@
scsi_unregister_module(MODULE_SCSI_DEV, &sg_template);
unregister_chrdev(SCSI_GENERIC_MAJOR, "sg");
+#ifdef CONFIG_PROC_FS
+ sg_proc_cleanup();
+#endif /* CONFIG_PROC_FS */
if(sg_dev_arr != NULL) {
/* Really worrying situation of writes still pending and get here */
/* Strategy: shorten timeout on release + wait on detach ... */
- kfree((char *) sg_dev_arr);
+ kfree((char *)sg_dev_arr);
sg_dev_arr = NULL;
}
sg_template.dev_max = 0;
@@ -1124,56 +1254,178 @@
scsi_add_timer(scpnt, scpnt->timeout_per_command,
scsi_old_times_out);
#else
- spin_unlock_irq(&sg_request_lock);
+ unsigned long flags = 0;
+ spin_lock_irqsave(&sg_request_lock, flags);
scsi_sleep(HZ); /* just sleep 1 second and hope ... */
- spin_lock_irq(&sg_request_lock);
+ spin_unlock_irqrestore(&sg_request_lock, flags);
#endif
}
-static int sg_start_req(Sg_request * srp, int max_buff_size,
- const char * inp, int num_write_xfer)
+static int sg_start_req(Sg_request * srp)
{
int res;
Sg_fd * sfp = srp->parentfp;
+ sg_io_hdr_t * hp = &srp->header;
+ int dxfer_len = (int)hp->dxfer_len;
Sg_scatter_hold * req_schp = &srp->data;
Sg_scatter_hold * rsv_schp = &sfp->reserve;
- SCSI_LOG_TIMEOUT(4, printk("sg_start_req: max_buff_size=%d\n",
- max_buff_size));
- if ((! sg_res_in_use(sfp)) && (max_buff_size <= rsv_schp->bufflen)) {
- sg_link_reserve(sfp, srp, max_buff_size);
- sg_write_xfer(req_schp, inp, num_write_xfer);
+ SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
+ if ((hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_len > 0) &&
+ (hp->dxfer_direction != SG_DXFER_NONE) && (0 == hp->iovec_count) &&
+ (! sfp->parentdp->device->host->unchecked_isa_dma)) {
+ res = sg_build_dir(srp, sfp, dxfer_len);
+ if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */
+ return res;
+ }
+ if ((! sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) {
+ sg_link_reserve(sfp, srp, dxfer_len);
}
else {
- res = sg_build_scat(req_schp, max_buff_size, sfp);
+ res = sg_build_indi(req_schp, sfp, dxfer_len);
if (res) {
sg_remove_scat(req_schp);
return res;
}
- sg_write_xfer(req_schp, inp, num_write_xfer);
}
return 0;
}
-static void sg_finish_rem_req(Sg_request * srp, char * outp,
- int num_read_xfer)
+static void sg_finish_rem_req(Sg_request * srp)
{
Sg_fd * sfp = srp->parentfp;
Sg_scatter_hold * req_schp = &srp->data;
SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n",
- (int)srp->res_used));
- if (num_read_xfer > 0)
- sg_read_xfer(req_schp, outp, num_read_xfer);
+ (int)srp->res_used));
+ sg_unmap_and(&srp->data, 1);
if (srp->res_used)
sg_unlink_reserve(sfp, srp);
- else
+ else
sg_remove_scat(req_schp);
sg_remove_request(sfp, srp);
}
-static int sg_build_scat(Sg_scatter_hold * schp, int buff_size,
- const Sg_fd * sfp)
+static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp)
+{
+ int mem_src, ret_sz;
+ int sg_bufflen = PAGE_SIZE;
+ int elem_sz = sizeof(struct scatterlist) + sizeof(char);
+ int mx_sc_elems = (sg_bufflen / elem_sz) - 1;
+
+ mem_src = SG_HEAP_KMAL;
+ schp->buffer = (struct scatterlist *)sg_malloc(sfp, sg_bufflen,
+ &ret_sz, &mem_src);
+ schp->buffer_mem_src = (char)mem_src;
+ if (! schp->buffer)
+ return -ENOMEM;
+ else if (ret_sz != sg_bufflen) {
+ sg_bufflen = ret_sz;
+ mx_sc_elems = (sg_bufflen / elem_sz) - 1;
+ }
+ schp->sglist_len = sg_bufflen;
+ memset(schp->buffer, 0, sg_bufflen);
+ return mx_sc_elems; /* number of scat_gath elements allocated */
+}
+
+static void sg_unmap_and(Sg_scatter_hold * schp, int free_also)
+{
+#ifdef SG_ALLOW_DIO
+ if (schp && schp->kiobp) {
+ if (schp->mapped) {
+ unmap_kiobuf(schp->kiobp);
+ schp->mapped = 0;
+ }
+ if (free_also) {
+ free_kiovec(1, &schp->kiobp);
+ schp->kiobp = NULL;
+ }
+ }
+#endif
+}
+
+static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len)
+{
+#ifdef SG_ALLOW_DIO
+ int res, k, split, offset, num, mx_sc_elems, rem_sz;
+ struct kiobuf * kp;
+ char * mem_src_arr;
+ struct scatterlist * sclp;
+ unsigned long addr, prev_addr;
+ sg_io_hdr_t * hp = &srp->header;
+ Sg_scatter_hold * schp = &srp->data;
+ int sg_tablesize = sfp->parentdp->sg_tablesize;
+
+ res = alloc_kiovec(1, &schp->kiobp);
+ if (0 != res) {
+ SCSI_LOG_TIMEOUT(5, printk("sg_build_dir: alloc_kiovec res=%d\n", res));
+ return 1;
+ }
+ res = map_user_kiobuf((SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0,
+ schp->kiobp, (unsigned long)hp->dxferp, dxfer_len);
+ if (0 != res) {
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_build_dir: map_user_kiobuf res=%d\n", res));
+ sg_unmap_and(schp, 1);
+ return 1;
+ }
+ schp->mapped = 1;
+ kp = schp->kiobp;
+ prev_addr = page_address(kp->maplist[0]);
+ for (k = 1, split = 0; k < kp->nr_pages; ++k, prev_addr = addr) {
+ addr = page_address(kp->maplist[k]);
+ if ((prev_addr + PAGE_SIZE) != addr) {
+ split = k;
+ break;
+ }
+ }
+ if (! split) {
+ schp->k_use_sg = 0;
+ schp->buffer = (void *)(page_address(kp->maplist[0]) + kp->offset);
+ schp->bufflen = dxfer_len;
+ schp->buffer_mem_src = SG_USER_MEM;
+ schp->b_malloc_len = dxfer_len;
+ hp->info |= SG_INFO_DIRECT_IO;
+ return 0;
+ }
+ mx_sc_elems = sg_build_sgat(schp, sfp);
+ if (mx_sc_elems <= 1) {
+ sg_unmap_and(schp, 1);
+ sg_remove_scat(schp);
+ return 1;
+ }
+ mem_src_arr = schp->buffer + (mx_sc_elems * sizeof(struct scatterlist));
+ for (k = 0, sclp = schp->buffer, rem_sz = dxfer_len;
+ (k < sg_tablesize) && (rem_sz > 0) && (k < mx_sc_elems);
+ ++k, ++sclp) {
+ offset = (0 == k) ? kp->offset : 0;
+ num = (rem_sz > (PAGE_SIZE - offset)) ? (PAGE_SIZE - offset) :
+ rem_sz;
+ sclp->address = (void *)(page_address(kp->maplist[k]) + offset);
+ sclp->length = num;
+ mem_src_arr[k] = SG_USER_MEM;
+ rem_sz -= num;
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_build_dir: k=%d, a=0x%p, len=%d, ms=%d\n",
+ k, sclp->address, num, mem_src_arr[k]));
+ }
+ schp->k_use_sg = k;
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_build_dir: k_use_sg=%d, rem_sz=%d\n", k, rem_sz));
+ schp->bufflen = dxfer_len;
+ if (rem_sz > 0) { /* must have failed */
+ sg_unmap_and(schp, 1);
+ sg_remove_scat(schp);
+ return 1; /* out of scatter gather elements, try indirect */
+ }
+ hp->info |= SG_INFO_DIRECT_IO;
+ return 0;
+#else
+ return 1;
+#endif /* SG_ALLOW_DIO */
+}
+
+static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
{
int ret_sz, mem_src;
int blk_size = buff_size;
@@ -1185,7 +1437,7 @@
++blk_size; /* don't know why */
/* round request up to next highest SG_SECTOR_SZ byte boundary */
blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK);
- SCSI_LOG_TIMEOUT(4, printk("sg_build_scat: buff_size=%d, blk_size=%d\n",
+ SCSI_LOG_TIMEOUT(4, printk("sg_build_indi: buff_size=%d, blk_size=%d\n",
buff_size, blk_size));
if (blk_size <= SG_SCATTER_SZ) {
mem_src = SG_HEAP_PAGE;
@@ -1193,10 +1445,10 @@
if (! p)
return -ENOMEM;
if (blk_size == ret_sz) { /* got it on the first attempt */
- schp->use_sg = 0;
+ schp->k_use_sg = 0;
schp->buffer = p;
schp->bufflen = blk_size;
- schp->mem_src = mem_src;
+ schp->buffer_mem_src = (char)mem_src;
schp->b_malloc_len = blk_size;
return 0;
}
@@ -1210,29 +1462,23 @@
/* Want some local declarations, so start new block ... */
{ /* lets try and build a scatter gather list */
struct scatterlist * sclp;
- int k, rem_sz, num, nxt;
- int sc_bufflen = PAGE_SIZE;
- int mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
+ int k, rem_sz, num;
+ int mx_sc_elems;
int sg_tablesize = sfp->parentdp->sg_tablesize;
int first = 1;
+ char * mem_src_arr;
- k = SG_HEAP_KMAL; /* want to protect mem_src, use k as scratch */
- schp->buffer = (struct scatterlist *)sg_malloc(sfp,
- sc_bufflen, &num, &k);
- schp->mem_src = (char)k;
/* N.B. ret_sz and mem_src carried into this block ... */
- if (! schp->buffer)
- return -ENOMEM;
- else if (num != sc_bufflen) {
- sc_bufflen = num;
- mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
- }
- schp->sglist_len = sc_bufflen;
- memset(schp->buffer, 0, sc_bufflen);
- for (k = 0, sclp = schp->buffer, rem_sz = blk_size, nxt =0;
- (k < sg_tablesize) && (rem_sz > 0) && (k < mx_sc_elems);
+ mx_sc_elems = sg_build_sgat(schp, sfp);
+ if (mx_sc_elems < 0)
+ return mx_sc_elems; /* most likely -ENOMEM */
+ mem_src_arr = schp->buffer +
+ (mx_sc_elems * sizeof(struct scatterlist));
+
+ for (k = 0, sclp = schp->buffer, rem_sz = blk_size;
+ (k < sg_tablesize) && (rem_sz > 0) && (k < mx_sc_elems);
++k, rem_sz -= ret_sz, ++sclp) {
- if (first)
+ if (first)
first = 0;
else {
num = (rem_sz > SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz;
@@ -1243,15 +1489,15 @@
}
sclp->address = p;
sclp->length = ret_sz;
- sclp->alt_address = (char *)(long)mem_src;
-
- SCSI_LOG_TIMEOUT(5,
- printk("sg_build_build: k=%d, a=0x%p, len=%d, ms=%d\n",
+ mem_src_arr[k] = mem_src;
+
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_build_build: k=%d, a=0x%p, len=%d, ms=%d\n",
k, sclp->address, ret_sz, mem_src));
} /* end of for loop */
- schp->use_sg = k;
- SCSI_LOG_TIMEOUT(5,
- printk("sg_build_scat: use_sg=%d, rem_sz=%d\n", k, rem_sz));
+ schp->k_use_sg = k;
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_build_indi: k_use_sg=%d, rem_sz=%d\n", k, rem_sz));
schp->bufflen = blk_size;
if (rem_sz > 0) /* must have failed */
return -ENOMEM;
@@ -1259,74 +1505,254 @@
return 0;
}
-static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp,
- int num_write_xfer)
+static int sg_write_xfer(Sg_request * srp)
{
- SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_write_xfer=%d, use_sg=%d\n",
- num_write_xfer, schp->use_sg));
- if ((! inp) || (num_write_xfer <= 0))
- return;
- if (schp->use_sg > 0) {
- int k, num;
- struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+ sg_io_hdr_t * hp = &srp->header;
+ Sg_scatter_hold * schp = &srp->data;
+ int num_xfer = 0;
+ int j, k, onum, usglen, ksglen, res, ok;
+ int iovec_count = (int)hp->iovec_count;
+ unsigned char * p;
+ unsigned char * up;
+ int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
+
+ if ((SG_DXFER_TO_DEV == hp->dxfer_direction) ||
+ (SG_DXFER_TO_FROM_DEV == hp->dxfer_direction)) {
+ num_xfer = (int)(new_interface ? hp->dxfer_len : hp->flags);
+ if (schp->bufflen < num_xfer)
+ num_xfer = schp->bufflen;
+ }
+ if ((num_xfer <= 0) || (new_interface && (SG_FLAG_NO_DXFER & hp->flags)))
+ return 0;
+
+ SCSI_LOG_TIMEOUT(4,
+ printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",
+ num_xfer, iovec_count, schp->k_use_sg));
+ if (iovec_count) {
+ onum = iovec_count;
+ if ((k = verify_area(VERIFY_READ, hp->dxferp,
+ size_sg_iovec * onum)))
+ return k;
+ }
+ else
+ onum = 1;
- for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
- num = (int)sclp->length;
- if (num > num_write_xfer) {
- __copy_from_user(sclp->address, inp, num_write_xfer);
- break;
+ if (0 == schp->k_use_sg) { /* kernel has single buffer */
+ if (SG_USER_MEM != schp->buffer_mem_src) { /* else nothing to do */
+
+ for (j = 0, p = schp->buffer; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);
+ if (res) return res;
+ usglen = (num_xfer > usglen) ? usglen : num_xfer;
+ __copy_from_user(p, up, usglen);
+ p += usglen;
+ num_xfer -= usglen;
+ if (num_xfer <= 0)
+ return 0;
}
- else {
- __copy_from_user(sclp->address, inp, num);
- num_write_xfer -= num;
- if (num_write_xfer <= 0)
+ }
+ }
+ else { /* kernel using scatter gather list */
+ struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+ char * mem_src_arr = sg_get_sgat_msa(schp);
+ ksglen = (int)sclp->length;
+ p = sclp->address;
+
+ for (j = 0, k = 0; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);
+ if (res) return res;
+
+ for (; (k < schp->k_use_sg) && p;
+ ++k, ++sclp, ksglen = (int)sclp->length, p = sclp->address) {
+ ok = (SG_USER_MEM != mem_src_arr[k]);
+ if (usglen <= 0)
+ break;
+ if (ksglen > usglen) {
+ if (usglen >= num_xfer) {
+ if (ok) __copy_from_user(p, up, num_xfer);
+ return 0;
+ }
+ if (ok) __copy_from_user(p, up, usglen);
+ p += usglen;
+ ksglen -= usglen;
break;
- inp += num;
+ }
+ else {
+ if (ksglen >= num_xfer) {
+ if (ok) __copy_from_user(p, up, num_xfer);
+ return 0;
+ }
+ if (ok) __copy_from_user(p, up, ksglen);
+ up += ksglen;
+ usglen -= ksglen;
+ }
}
}
}
- else
- __copy_from_user(schp->buffer, inp, num_write_xfer);
+ return 0;
+}
+
+static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
+ int wr_xf, int * countp, unsigned char ** up)
+{
+ int num_xfer = (int)hp->dxfer_len;
+ unsigned char * p;
+ int count, k;
+ sg_iovec_t u_iovec;
+
+ if (0 == sg_num) {
+ p = (unsigned char *)hp->dxferp;
+ if (wr_xf && ('\0' == hp->interface_id))
+ count = (int)hp->flags; /* holds "old" input_size */
+ else
+ count = num_xfer;
+ }
+ else {
+ __copy_from_user(&u_iovec,
+ (unsigned char *)hp->dxferp + (ind * size_sg_iovec),
+ size_sg_iovec);
+ p = (unsigned char *)u_iovec.iov_base;
+ count = (int)u_iovec.iov_len;
+ }
+ if ((k = verify_area(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count)))
+ return k;
+ if (up) *up = p;
+ if (countp) *countp = count;
+ return 0;
+}
+
+static char * sg_get_sgat_msa(Sg_scatter_hold * schp)
+{
+ int elem_sz = sizeof(struct scatterlist) + sizeof(char);
+ int mx_sc_elems = (schp->sglist_len / elem_sz) - 1;
+ return schp->buffer + (sizeof(struct scatterlist) * mx_sc_elems);
}
static void sg_remove_scat(Sg_scatter_hold * schp)
{
- SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: use_sg=%d\n", schp->use_sg));
- if(schp->use_sg > 0) {
+ SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n",
+ schp->k_use_sg));
+ if (schp->buffer && schp->sglist_len) {
int k, mem_src;
struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+ char * mem_src_arr = sg_get_sgat_msa(schp);
- for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
- mem_src = (int)(long)sclp->alt_address;
- SCSI_LOG_TIMEOUT(5,
- printk("sg_remove_scat: k=%d, a=0x%p, len=%d, ms=%d\n",
+ for (k = 0; (k < schp->k_use_sg) && sclp->address; ++k, ++sclp) {
+ mem_src = mem_src_arr[k];
+ SCSI_LOG_TIMEOUT(5,
+ printk("sg_remove_scat: k=%d, a=0x%p, len=%d, ms=%d\n",
k, sclp->address, sclp->length, mem_src));
sg_free(sclp->address, sclp->length, mem_src);
sclp->address = NULL;
sclp->length = 0;
}
- sg_free(schp->buffer, schp->sglist_len, schp->mem_src);
+ sg_free(schp->buffer, schp->sglist_len, schp->buffer_mem_src);
}
else if (schp->buffer)
- sg_free(schp->buffer, schp->b_malloc_len, schp->mem_src);
- schp->buffer = NULL;
- schp->bufflen = 0;
- schp->use_sg = 0;
- schp->sglist_len = 0;
+ sg_free(schp->buffer, schp->b_malloc_len, schp->buffer_mem_src);
+ memset(schp, 0, sizeof(*schp));
+}
+
+static int sg_read_xfer(Sg_request * srp)
+{
+ sg_io_hdr_t * hp = &srp->header;
+ Sg_scatter_hold * schp = &srp->data;
+ int num_xfer = 0;
+ int j, k, onum, usglen, ksglen, res, ok;
+ int iovec_count = (int)hp->iovec_count;
+ unsigned char * p;
+ unsigned char * up;
+ int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
+
+ if ((SG_DXFER_FROM_DEV == hp->dxfer_direction) ||
+ (SG_DXFER_TO_FROM_DEV == hp->dxfer_direction)) {
+ num_xfer = hp->dxfer_len;
+ if (schp->bufflen < num_xfer)
+ num_xfer = schp->bufflen;
+ }
+ if ((num_xfer <= 0) || (new_interface && (SG_FLAG_NO_DXFER & hp->flags)))
+ return 0;
+
+ SCSI_LOG_TIMEOUT(4,
+ printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",
+ num_xfer, iovec_count, schp->k_use_sg));
+ if (iovec_count) {
+ onum = iovec_count;
+ if ((k = verify_area(VERIFY_READ, hp->dxferp,
+ size_sg_iovec * onum)))
+ return k;
+ }
+ else
+ onum = 1;
+
+ if (0 == schp->k_use_sg) { /* kernel has single buffer */
+ if (SG_USER_MEM != schp->buffer_mem_src) { /* else nothing to do */
+
+ for (j = 0, p = schp->buffer; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);
+ if (res) return res;
+ usglen = (num_xfer > usglen) ? usglen : num_xfer;
+ __copy_to_user(up, p, usglen);
+ p += usglen;
+ num_xfer -= usglen;
+ if (num_xfer <= 0)
+ return 0;
+ }
+ }
+ }
+ else { /* kernel using scatter gather list */
+ struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+ char * mem_src_arr = sg_get_sgat_msa(schp);
+ ksglen = (int)sclp->length;
+ p = sclp->address;
+
+ for (j = 0, k = 0; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);
+ if (res) return res;
+
+ for (; (k < schp->k_use_sg) && p;
+ ++k, ++sclp, ksglen = (int)sclp->length, p = sclp->address) {
+ ok = (SG_USER_MEM != mem_src_arr[k]);
+ if (usglen <= 0)
+ break;
+ if (ksglen > usglen) {
+ if (usglen >= num_xfer) {
+ if (ok) __copy_to_user(up, p, num_xfer);
+ return 0;
+ }
+ if (ok) __copy_to_user(up, p, usglen);
+ p += usglen;
+ ksglen -= usglen;
+ break;
+ }
+ else {
+ if (ksglen >= num_xfer) {
+ if (ok) __copy_to_user(up, p, num_xfer);
+ return 0;
+ }
+ if (ok) __copy_to_user(up, p, ksglen);
+ up += ksglen;
+ usglen -= ksglen;
+ }
+ }
+ }
+ }
+ return 0;
}
-static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
- int num_read_xfer)
+static void sg_read_oxfer(Sg_request * srp, char * outp, int num_read_xfer)
{
- SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_read_xfer=%d\n",
- num_read_xfer));
+ Sg_scatter_hold * schp = &srp->data;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n",
+ num_read_xfer));
if ((! outp) || (num_read_xfer <= 0))
return;
- if(schp->use_sg > 0) {
+ if(schp->k_use_sg > 0) {
int k, num;
struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
- for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
+ for (k = 0; (k < schp->k_use_sg) && sclp->address; ++k, ++sclp) {
num = (int)sclp->length;
if (num > num_read_xfer) {
__copy_to_user(outp, sclp->address, num_read_xfer);
@@ -1349,11 +1775,11 @@
{
Sg_scatter_hold * schp = &sfp->reserve;
- SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size));
+ SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size));
do {
if (req_size < PAGE_SIZE)
req_size = PAGE_SIZE;
- if (0 == sg_build_scat(schp, req_size, sfp))
+ if (0 == sg_build_indi(schp, sfp, req_size))
return;
else
sg_remove_scat(schp);
@@ -1366,13 +1792,13 @@
Sg_scatter_hold * req_schp = &srp->data;
Sg_scatter_hold * rsv_schp = &sfp->reserve;
- SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));
- if (rsv_schp->use_sg > 0) {
+ SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));
+ if (rsv_schp->k_use_sg > 0) {
int k, num;
int rem = size;
struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
- for (k = 0; k < rsv_schp->use_sg; ++k, ++sclp) {
+ for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) {
num = (int)sclp->length;
if (rem <= num) {
sfp->save_scat_len = num;
@@ -1382,23 +1808,23 @@
else
rem -= num;
}
- if (k < rsv_schp->use_sg) {
- req_schp->use_sg = k + 1; /* adjust scatter list length */
+ if (k < rsv_schp->k_use_sg) {
+ req_schp->k_use_sg = k + 1; /* adjust scatter list length */
req_schp->bufflen = size;
req_schp->sglist_len = rsv_schp->sglist_len;
req_schp->buffer = rsv_schp->buffer;
- req_schp->mem_src = rsv_schp->mem_src;
+ req_schp->buffer_mem_src = rsv_schp->buffer_mem_src;
req_schp->b_malloc_len = rsv_schp->b_malloc_len;
}
else
- SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n"));
+ SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n"));
}
else {
- req_schp->use_sg = 0;
+ req_schp->k_use_sg = 0;
req_schp->bufflen = size;
req_schp->buffer = rsv_schp->buffer;
- req_schp->mem_src = rsv_schp->mem_src;
- req_schp->use_sg = rsv_schp->use_sg;
+ req_schp->buffer_mem_src = rsv_schp->buffer_mem_src;
+ req_schp->k_use_sg = rsv_schp->k_use_sg;
req_schp->b_malloc_len = rsv_schp->b_malloc_len;
}
srp->res_used = 1;
@@ -1409,19 +1835,19 @@
Sg_scatter_hold * req_schp = &srp->data;
Sg_scatter_hold * rsv_schp = &sfp->reserve;
- SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->use_sg=%d\n",
- (int)req_schp->use_sg));
- if (rsv_schp->use_sg > 0) {
+ SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n",
+ (int)req_schp->k_use_sg));
+ if (rsv_schp->k_use_sg > 0) {
struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
- if (sfp->save_scat_len > 0)
- (sclp + (req_schp->use_sg - 1))->length =
+ if (sfp->save_scat_len > 0)
+ (sclp + (req_schp->k_use_sg - 1))->length =
(unsigned)sfp->save_scat_len;
else
SCSI_LOG_TIMEOUT(1, printk(
- "sg_unlink_reserve: BAD save_scat_len\n"));
+ "sg_unlink_reserve: BAD save_scat_len\n"));
}
- req_schp->use_sg = 0;
+ req_schp->k_use_sg = 0;
req_schp->bufflen = 0;
req_schp->buffer = NULL;
req_schp->sglist_len = 0;
@@ -1434,8 +1860,8 @@
Sg_request * resp = NULL;
resp = sfp->headrp;
- while (resp) {
- if ((! resp->my_cmdp) &&
+ while (resp) { /* look for requests that are ready + not SG_IO owned */
+ if (resp->done && (! resp->sg_io_owned) &&
((-1 == pack_id) || (resp->header.pack_id == pack_id)))
return resp;
resp = resp->nextrp;
@@ -1477,15 +1903,21 @@
resp->parentfp = sfp;
resp->nextrp = NULL;
resp->res_used = 0;
+ resp->orphan = 0;
+ resp->sg_io_owned = 0;
+ resp->done = 0;
memset(&resp->data, 0, sizeof(Sg_scatter_hold));
- memset(&resp->header, 0, sizeof(struct sg_header));
+ memset(&resp->header, 0, size_sg_io_hdr);
+ resp->header.duration = jiffies;
resp->my_cmdp = NULL;
+ resp->data.kiobp = NULL;
+ resp->data.mapped = 0;
}
return resp;
}
/* Return of 1 for found; 0 for not found */
-static int sg_remove_request(Sg_fd * sfp, const Sg_request * srp)
+static int sg_remove_request(Sg_fd * sfp, Sg_request * srp)
{
Sg_request * prev_rp;
Sg_request * rp;
@@ -1509,30 +1941,23 @@
return 0;
}
-static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev, int get_reserved)
+static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
{
Sg_fd * sfp;
- if (sdp->merge_fd) {
- ++sdp->merge_fd;
- return sdp->headfp;
- }
sfp = (Sg_fd *)sg_low_malloc(sizeof(Sg_fd), 0, SG_HEAP_KMAL, 0);
- if (sfp) {
- memset(sfp, 0, sizeof(Sg_fd));
- sfp->my_mem_src = SG_HEAP_KMAL;
- init_waitqueue_head(&sfp->read_wait);
- init_waitqueue_head(&sfp->write_wait);
- }
- else
+ if (! sfp)
return NULL;
-
+ memset(sfp, 0, sizeof(Sg_fd));
+ sfp->fd_mem_src = SG_HEAP_KMAL;
+ init_waitqueue_head(&sfp->read_wait);
+
sfp->timeout = SG_DEFAULT_TIMEOUT;
sfp->force_packid = SG_DEF_FORCE_PACK_ID;
sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
sdp->device->host->unchecked_isa_dma : 1;
sfp->cmd_q = SG_DEF_COMMAND_Q;
- sfp->underrun_flag = SG_DEF_UNDERRUN_FLAG;
+ sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
sfp->parentdp = sdp;
if (! sdp->headfp)
sdp->headfp = sfp;
@@ -1543,13 +1968,10 @@
pfp->nextfp = sfp;
}
SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p, m_s=%d\n",
- sfp, (int)sfp->my_mem_src));
- if (get_reserved) {
- sg_build_reserve(sfp, SG_DEF_RESERVED_SIZE);
- sg_big_buff = sfp->reserve.bufflen; /* sysctl shows most recent size */
- SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, use_sg=%d\n",
- sfp->reserve.bufflen, sfp->reserve.use_sg));
- }
+ sfp, (int)sfp->fd_mem_src));
+ sg_build_reserve(sfp, sg_big_buff);
+ SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n",
+ sfp->reserve.bufflen, sfp->reserve.k_use_sg));
return sfp;
}
@@ -1560,17 +1982,13 @@
int dirty = 0;
int res = 0;
- if (sdp->merge_fd) {
- if (--sdp->merge_fd)
- return 0; /* if merge_fd then dec merge_fd counter */
- }
srp = sfp->headrp;
if (srp) {
-/* Need to stop sg_command_done() playing with this list during this loop */
+/* Need to stop sg_cmd_done_bh() playing with this list during this loop */
while (srp) {
tsrp = srp->nextrp;
- if (! srp->my_cmdp)
- sg_finish_rem_req(srp, NULL, 0);
+ if (srp->done)
+ sg_finish_rem_req(srp);
else
++dirty;
srp = tsrp;
@@ -1592,13 +2010,13 @@
}
}
if (sfp->reserve.bufflen > 0) {
-SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: bufflen=%d, use_sg=%d\n",
- (int)sfp->reserve.bufflen, (int)sfp->reserve.use_sg));
+SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: bufflen=%d, k_use_sg=%d\n",
+ (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg));
sg_remove_scat(&sfp->reserve);
}
sfp->parentdp = NULL;
- SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: sfp=0x%p\n", sfp));
- sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->my_mem_src);
+ SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: sfp=0x%p\n", sfp));
+ sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src);
res = 1;
}
else {
@@ -1621,6 +2039,18 @@
return 0;
}
+static int sg_dio_in_use(const Sg_fd * sfp)
+{
+ const Sg_request * srp = sfp->headrp;
+
+ while (srp) {
+ if ((! srp->done) && srp->data.kiobp)
+ return 1;
+ srp = srp->nextrp;
+ }
+ return 0;
+}
+
/* If retSzp==NULL want exact size or fail */
/* sg_low_malloc() should always be called from a process context allowing
GFP_KERNEL to be used instead of GFP_ATOMIC */
@@ -1636,9 +2066,6 @@
/* Seen kmalloc(..,GFP_KERNEL) hang for 40 secs! */
resp = kmalloc(rqSz, page_mask);
if (resp && retSzp) *retSzp = rqSz;
-#ifdef SG_DEBUG
- if (resp) ++sg_num_kmal;
-#endif
return resp;
}
if (SG_HEAP_POOL == mem_src) {
@@ -1657,9 +2084,6 @@
if (resp) {
if (retSzp) *retSzp = rqSz;
sg_pool_secs_avail -= num_sect;
-#ifdef SG_DEBUG
- ++sg_num_pool;
-#endif
return resp;
}
}
@@ -1684,16 +2108,13 @@
resSz = a_size;
}
if (retSzp) *retSzp = resSz;
-#ifdef SG_DEBUG
- if (resp) ++sg_num_page;
-#endif
}
else
printk("sg_low_malloc: bad mem_src=%d, rqSz=%df\n", mem_src, rqSz);
return resp;
}
-static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp,
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp,
int * mem_srcp)
{
char * resp = NULL;
@@ -1705,7 +2126,7 @@
int low_dma = sfp->low_dma;
int l_ms = -1; /* invalid value */
- switch (*mem_srcp)
+ switch (*mem_srcp)
{
case SG_HEAP_PAGE:
l_ms = (size < PAGE_SIZE) ? SG_HEAP_POOL : SG_HEAP_PAGE;
@@ -1738,45 +2159,54 @@
}
if (resp) *mem_srcp = l_ms;
}
- SCSI_LOG_TIMEOUT(6, printk("sg_malloc: size=%d, ms=%d, ret=0x%p\n",
+ SCSI_LOG_TIMEOUT(6, printk("sg_malloc: size=%d, ms=%d, ret=0x%p\n",
size, *mem_srcp, resp));
return resp;
}
static void sg_low_free(char * buff, int size, int mem_src)
{
- if (! buff)
- return;
- if (SG_HEAP_POOL == mem_src) {
- int num_sect = size / SG_SECTOR_SZ;
- scsi_free(buff, size);
- sg_pool_secs_avail += num_sect;
- }
- else if (SG_HEAP_KMAL == mem_src)
- kfree(buff); /* size not used */
- else if (SG_HEAP_PAGE == mem_src) {
- int order, a_size;
-
- for (order = 0, a_size = PAGE_SIZE;
- a_size < size; order++, a_size <<= 1)
- ;
- free_pages((unsigned long)buff, order);
- }
- else
- printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n",
+ if (! buff) return;
+ switch (mem_src) {
+ case SG_HEAP_POOL:
+ {
+ int num_sect = size / SG_SECTOR_SZ;
+
+ scsi_free(buff, size);
+ sg_pool_secs_avail += num_sect;
+ }
+ break;
+ case SG_HEAP_KMAL:
+ kfree(buff); /* size not used */
+ break;
+ case SG_HEAP_PAGE:
+ {
+ int order, a_size;
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ free_pages((unsigned long)buff, order);
+ }
+ break;
+ case SG_USER_MEM:
+ break; /* nothing to do */
+ default:
+ printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n",
mem_src, buff, size);
+ break;
+ }
}
static void sg_free(char * buff, int size, int mem_src)
{
- SCSI_LOG_TIMEOUT(6,
+ SCSI_LOG_TIMEOUT(6,
printk("sg_free: buff=0x%p, size=%d\n", buff, size));
if ((! buff) || (size <= 0))
;
else
sg_low_free(buff, size, mem_src);
}
-
+
static void sg_clr_scpnt(Scsi_Cmnd * SCpnt)
{
SCpnt->use_sg = 0;
@@ -1787,3 +2217,391 @@
SCpnt->request.rq_dev = MKDEV(0, 0); /* "sg" _disowns_ command blk */
}
+static int sg_ms_to_jif(unsigned int msecs)
+{
+ if ((UINT_MAX / 2U) < msecs)
+ return INT_MAX; /* special case, set largest possible */
+ else
+ return ((int)msecs < (INT_MAX / 1000)) ? (((int)msecs * HZ) / 1000)
+ : (((int)msecs / 1000) * HZ);
+}
+
+static unsigned sg_jif_to_ms(int jifs)
+{
+ if (jifs <= 0)
+ return 0U;
+ else {
+ unsigned int j = (unsigned int)jifs;
+ return (j < (UINT_MAX / 1000)) ? ((j * 1000) / HZ) : ((j / HZ) * 1000);
+ }
+}
+
+static unsigned char allow_ops[] = {TEST_UNIT_READY, INQUIRY,
+READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12};
+
+static int sg_allow_access(unsigned char opcode, char dev_type)
+{
+ int k;
+
+ if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */
+ return 1;
+ for (k = 0; k < sizeof(allow_ops); ++k) {
+ if (opcode == allow_ops[k])
+ return 1;
+ }
+ return 0;
+}
+
+
+static int sg_last_dev()
+{
+ int k;
+ for (k = sg_template.dev_max - 1; k >= 0; --k) {
+ if (sg_dev_arr[k].device)
+ return k + 1;
+ }
+ return 0; /* origin 1 */
+}
+
+#ifdef CONFIG_PROC_FS
+
+static struct proc_dir_entry * sg_proc_sgp = NULL;
+
+static const char * sg_proc_sg_dirname = "sg";
+static const char * sg_proc_leaf_names[] = {"def_reserved_size", "debug",
+ "devices", "device_hdr", "device_strs",
+ "hosts", "host_hdr", "host_strs", "version"};
+
+static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_dressz_write(struct file * filp, const char * buffer,
+ unsigned long count, void * data);
+static int sg_proc_debug_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_debug_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_dev_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_dev_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_devhdr_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_devstrs_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_devstrs_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_host_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_host_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_hosthdr_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_hosthdr_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_hoststrs_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_hoststrs_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static int sg_proc_version_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data);
+static int sg_proc_version_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size);
+static read_proc_t * sg_proc_leaf_reads[] = {
+ sg_proc_dressz_read, sg_proc_debug_read,
+ sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read,
+ sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read,
+ sg_proc_version_read};
+static write_proc_t * sg_proc_leaf_writes[] = {
+ sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0};
+
+#define PRINT_PROC(fmt,args...) \
+ do { \
+ *len += sprintf(buffer + *len, fmt, ##args); \
+ if (*begin + *len > offset + size) \
+ return 0; \
+ if (*begin + *len < offset) { \
+ *begin += *len; \
+ *len = 0; \
+ } \
+ } while(0)
+
+#define SG_PROC_READ_FN(infofp) \
+ do { \
+ int len = 0; \
+ off_t begin = 0; \
+ *eof = infofp(buffer, &len, &begin, offset, size); \
+ if (offset >= (begin + len)) \
+ return 0; \
+ *start = buffer + ((begin > offset) ? \
+ (begin - offset) : (offset - begin)); \
+ return (size < (begin + len - offset)) ? \
+ size : begin + len - offset; \
+ } while(0)
+
+
+static int sg_proc_init()
+{
+ int k, mask;
+ int leaves = sizeof(sg_proc_leaf_names) / sizeof(sg_proc_leaf_names[0]);
+ struct proc_dir_entry * pdep;
+
+ if (! proc_scsi)
+ return 1;
+ sg_proc_sgp = create_proc_entry(sg_proc_sg_dirname,
+ S_IFDIR | S_IRUGO | S_IXUGO, proc_scsi);
+ if (! sg_proc_sgp)
+ return 1;
+ for (k = 0; k < leaves; ++k) {
+ mask = sg_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR : S_IRUGO;
+ pdep = create_proc_entry(sg_proc_leaf_names[k], mask, sg_proc_sgp);
+ if (pdep) {
+ pdep->read_proc = sg_proc_leaf_reads[k];
+ if (sg_proc_leaf_writes[k])
+ pdep->write_proc = sg_proc_leaf_writes[k];
+ }
+ }
+ return 0;
+}
+
+static void sg_proc_cleanup()
+{
+ int k;
+ int leaves = sizeof(sg_proc_leaf_names) / sizeof(sg_proc_leaf_names[0]);
+
+ if ((! proc_scsi) || (! sg_proc_sgp))
+ return;
+ for (k = 0; k < leaves; ++k)
+ remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp);
+ remove_proc_entry(sg_proc_sg_dirname, proc_scsi);
+}
+
+static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_dressz_info); }
+
+static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ PRINT_PROC("%d\n", sg_big_buff);
+ return 1;
+}
+
+static int sg_proc_dressz_write(struct file * filp, const char * buffer,
+ unsigned long count, void * data)
+{
+ int num;
+ unsigned long k = ULONG_MAX;
+ char buff[11];
+
+ if (! capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ num = (count < 10) ? count : 10;
+ copy_from_user(buff, buffer, num);
+ buff[count] = '\0';
+ k = simple_strtoul(buff, 0, 10);
+ if (k <= 1048576) {
+ sg_big_buff = k;
+ return count;
+ }
+ return -ERANGE;
+}
+
+static int sg_proc_debug_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_debug_info); }
+
+static int sg_proc_debug_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ const Sg_device * sdp = sg_dev_arr;
+ const sg_io_hdr_t * hp;
+ int j, max_dev;
+
+ if (NULL == sg_dev_arr) {
+ PRINT_PROC("sg_dev_arr NULL, death is imminent\n");
+ return 1;
+ }
+ max_dev = sg_last_dev();
+ PRINT_PROC("dev_max=%d max_active_device=%d (origin 1)\n",
+ sg_template.dev_max, max_dev);
+ PRINT_PROC(" scsi_dma_free_sectors=%u sg_pool_secs_aval=%d "
+ "def_reserved_size=%d\n",
+ scsi_dma_free_sectors, sg_pool_secs_avail, sg_big_buff);
+ max_dev = sg_last_dev();
+ for (j = 0; j < max_dev; ++j, ++sdp) {
+ if (sdp) {
+ Sg_fd * fp;
+ Sg_request * srp;
+ struct scsi_device * scsidp;
+ int dev, k, blen, usg, crep;
+
+ if (! (scsidp = sdp->device)) {
+ PRINT_PROC("device %d detached ??\n", j);
+ continue;
+ }
+ dev = MINOR(sdp->i_rdev);
+ crep = 'a' + dev;
+
+ PRINT_PROC(" >>> device=%d(sg%c) ", dev, crep > 126 ? '?' : crep);
+ PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d sg_tablesize=%d"
+ " excl=%d\n", scsidp->host->host_no, scsidp->channel,
+ scsidp->id, scsidp->lun, scsidp->host->hostt->emulated,
+ sdp->sg_tablesize, sdp->exclude);
+ fp = sdp->headfp;
+ for (k = 1; fp; fp = fp->nextfp, ++k) {
+ PRINT_PROC(" FD(%d): timeout=%d bufflen=%d "
+ "(res)sgat=%d low_dma=%d\n",
+ k, fp->timeout, fp->reserve.bufflen,
+ (int)fp->reserve.k_use_sg, (int)fp->low_dma);
+ PRINT_PROC(" cmd_q=%d f_packid=%d k_orphan=%d closed=%d\n",
+ (int)fp->cmd_q, (int)fp->force_packid,
+ (int)fp->keep_orphan, (int)fp->closed);
+ srp = fp->headrp;
+ if (NULL == srp)
+ PRINT_PROC(" No requests active\n");
+ while (srp) {
+ hp = &srp->header;
+/* stop indenting so far ... */
+ PRINT_PROC(srp->res_used ? " reserved_buff>> " :
+ ((SG_INFO_DIRECT_IO_MASK & hp->info) ? " dio>> " : " "));
+ blen = srp->my_cmdp ? srp->my_cmdp->bufflen : srp->data.bufflen;
+ usg = srp->my_cmdp ? srp->my_cmdp->use_sg : srp->data.k_use_sg;
+ PRINT_PROC(srp->done ? "rcv: id=%d" : (srp->my_cmdp ? "act: id=%d" :
+ "prior: id=%d"), srp->header.pack_id);
+ if (! srp->res_used) PRINT_PROC(" blen=%d", blen);
+ if (srp->done)
+ PRINT_PROC(" dur=%d", sg_jif_to_ms(hp->duration));
+ else
+ PRINT_PROC(" t_o/elap=%d/%d", ((hp->interface_id == '\0') ?
+ sg_jif_to_ms(fp->timeout) : hp->timeout),
+ sg_jif_to_ms(hp->duration ? (jiffies - hp->duration) : 0));
+ PRINT_PROC(" sgat=%d op=0x%02x\n", usg, (int)srp->data.cmd_opcode);
+ srp = srp->nextrp;
+/* reset indenting */
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int sg_proc_dev_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_dev_info); }
+
+static int sg_proc_dev_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ const Sg_device * sdp = sg_dev_arr;
+ int j, max_dev;
+ struct scsi_device * scsidp;
+
+ max_dev = sg_last_dev();
+ for (j = 0; j < max_dev; ++j, ++sdp) {
+ if (sdp) {
+ if (! (scsidp = sdp->device)) {
+ PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
+ continue;
+ }
+ PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ scsidp->host->host_no, scsidp->channel, scsidp->id,
+ scsidp->lun, (int)scsidp->type, (int)scsidp->disconnect,
+ (int)scsidp->queue_depth, (int)scsidp->tagged_queue);
+ }
+ }
+ return 1;
+}
+
+static int sg_proc_devhdr_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_devhdr_info); }
+
+static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ PRINT_PROC("host\tchan\tid\tlun\ttype\tdiscon\tqdepth\ttq\n");
+ return 1;
+}
+
+static int sg_proc_devstrs_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_devstrs_info); }
+
+static int sg_proc_devstrs_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ const Sg_device * sdp = sg_dev_arr;
+ int j, max_dev;
+ struct scsi_device * scsidp;
+
+ max_dev = sg_last_dev();
+ for (j = 0; j < max_dev; ++j, ++sdp) {
+ if (sdp) {
+ if ((scsidp = sdp->device))
+ PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n",
+ scsidp->vendor, scsidp->model, scsidp->rev);
+ else
+ PRINT_PROC("<no active device>\n");
+ }
+ }
+ return 1;
+}
+
+static int sg_proc_host_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_host_info); }
+
+static int sg_proc_host_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ struct Scsi_Host * shp;
+
+ for (shp = scsi_hostlist; shp; shp = shp->next)
+ PRINT_PROC("%u\t%hu\t%hd\t%hu\t%d\t%d\n",
+ shp->unique_id, shp->host_busy, shp->cmd_per_lun,
+ shp->sg_tablesize, (int)shp->unchecked_isa_dma,
+ (int)shp->hostt->emulated);
+ return 1;
+}
+
+static int sg_proc_hosthdr_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_hosthdr_info); }
+
+static int sg_proc_hosthdr_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ PRINT_PROC("uid\tbusy\tcpl\tscatg\tisa\temul\n");
+ return 1;
+}
+
+static int sg_proc_hoststrs_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_hoststrs_info); }
+
+static int sg_proc_hoststrs_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ struct Scsi_Host * shp;
+
+ for (shp = scsi_hostlist; shp; shp = shp->next)
+ PRINT_PROC("%s\n", shp->hostt->info ? shp->hostt->info(shp) :
+ (shp->hostt->name ? shp->hostt->name : "<no name>"));
+ return 1;
+}
+
+static int sg_proc_version_read(char * buffer, char ** start, off_t offset,
+ int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_version_info); }
+
+static int sg_proc_version_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ PRINT_PROC("%d\t%s\n", sg_version_num, sg_version_str);
+ return 1;
+}
+#endif /* CONFIG_PROC_FS */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)