Re: SG_BIG_BUFF, glibc 2.1 weirdness ...

abel deuring (a.deuring@satzbau-gmbh.de)
Fri, 29 Oct 1999 20:56:45 +0200

Dies ist eine mehrteilige Nachricht im MIME-Format.
--------------5D2267F9E78A0A1B29D131D6
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

abel deuring wrote:

> At the end of the mail are some modifications to sanei_scsi.c to
> get a "better cooperation" with the new Linux SG driver.

As Ricardo Ferreira discovered, the patches got a little bit damaged, so
here they are again (hopefully better useable...)

Abel
--------------5D2267F9E78A0A1B29D131D6
Content-Type: text/plain; charset=us-ascii; name="sanei_scsi-diff-sg2.1.34"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="sanei_scsi-diff-sg2.1.34"

--- sane-1.0.1-orig/sanei/sanei_scsi.c Sat Apr 17 22:18:18 1999
+++ sane-1.0.1/sanei/sanei_scsi.c Thu Oct 28 22:10:21 1999
@@ -194,6 +194,76 @@
#endif

int sanei_scsi_max_request_size = MAX_DATA;
+#if USE == LINUX_INTERFACE
+/* the following #defines follow Douglas Gilbert's sample code
+ to maintain run time compatibility with the old and the
+ new SG driver for Linux
+*/
+#ifndef SG_SET_COMMAND_Q
+#define SG_SET_COMMAND_Q 0x2271
+#endif
+#ifndef SG_SET_RESERVED_SIZE
+#define SG_SET_RESERVED_SIZE 0x2275
+#endif
+#ifndef SG_GET_RESERVED_SIZE
+#define SG_GET_RESERVED_SIZE 0x2272
+#endif
+#ifndef SG_GET_SCSI_ID
+#define SG_GET_SCSI_ID 0x2276
+#endif
+#ifndef SG_GET_VERSION_NUM
+#define SG_GET_VERSION_NUM 0x2282
+#endif
+
+#ifndef SCSIBUFFERSIZE
+#define SCSIBUFFERSIZE (128 * 1024)
+#endif
+
+/* the struct returned by the SG ioctl call SG_GET_SCSI_ID changed
+ from version 2.1.34 to 2.1.35, and we need the informations from
+ the field s_queue_depth, which was introduced in 2.1.35.
+ To get this file compiling also with older versions of sg.h, the
+ struct is re-defined here.
+*/
+typedef struct xsg_scsi_id {
+ int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
+ int channel;
+ int scsi_id; /* scsi id of target device */
+ int lun;
+ int scsi_type; /* TYPE_... defined in scsi/scsi.h */
+ short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
+ short d_queue_depth;/* device (or adapter) maximum queue length */
+ int unused1; /* probably find a good use, set 0 for now */
+ int unused2; /* ditto */
+} SG_scsi_id;
+
+typedef struct req
+ {
+ struct req *next;
+ int fd;
+ u_int running:1, done:1;
+ SANE_Status status;
+ size_t *dst_len;
+ void *dst;
+ struct
+ {
+ struct sg_header hdr;
+ /* Make sure this is the last element, the real size is
+ SG_BIG_BUFF and machine dependant */
+ u_int8_t data[1];
+ }
+ cdb;
+ }
+req;
+
+typedef struct Fdparms
+ {
+ int sg_queue_used, sg_queue_max;
+ req *sane_qhead, *sane_qtail, *sane_free_list;
+ }
+fdparms;
+
+#endif

#if USE == FREEBSD_CAM_INTERFACE
# define CAM_MAXDEVS 128
@@ -590,12 +660,25 @@

#endif /* USE_OS2_INTERFACE */

+static int num_alloced = 0;
+
+#if USE == LINUX_INTERFACE
+
+SANE_Status
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+
+#else
+
SANE_Status
sanei_scsi_open (const char *dev, int *fdp,
SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+
+#endif
+
{
u_int bus = 0, target = 0, lun = 0, fake_fd = 0;
- static int num_alloced = 0;
char *real_dev = 0;
void *pdata = 0;
int fd;
@@ -621,6 +704,7 @@
sanei_scsi_max_request_size = atoi (buf);
DBG (1, "sanei_scsi_open: sanei_scsi_max_request_size=%d bytes\n",
sanei_scsi_max_request_size);
+ close(fd);
}
}
#endif
@@ -913,6 +997,109 @@
}
}
#endif /* SGIOCSTL */
+#if USE == LINUX_INTERFACE
+ {
+ SG_scsi_id sid;
+ int ioctl_val, sg_version;
+ int real_buffersize;
+ fdparms *fdpa = 0;
+
+ pdata = fdpa = malloc(sizeof(fdparms));
+ if (!pdata)
+ {
+ close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(fdpa, 0, sizeof(fdparms));
+ /* default: allow only one command to be sent to the SG driver
+ */
+ fdpa->sg_queue_max = 1;
+
+ /* Try to read the SG version. If the ioctl call is successful,
+ we have the new SG driver, and we can increase the buffer size
+ using another ioctl call.
+ If we have SG version 2.1.35 or above, we can additionally enable
+ command queueing.
+ */
+ if (0 == ioctl(fd, SG_GET_VERSION_NUM, &sg_version))
+ {
+ DBG(1, "sanei_scsi_open: SG driver version: %i\n", sg_version);
+
+ /* try to reserve a SG buffer of the size specified by *buffersize
+ */
+ ioctl(fd, SG_SET_RESERVED_SIZE, buffersize);
+
+ /* the set call may not be able to allocate as much memory
+ as requested, thus we read the actual buffer size.
+
+ NOTE: sanei_scsi_max_request_size is a global variable
+ used for all devices/file handles, while version 2.0 and
+ above of the SG driver allocate buffer memory for each
+ opened file separately. Therefore, we have a possible
+ inconsistency, if more than one file is opened and
+ if the SG_GET_RESERVED_SIZE return different buffer sizes
+ for different file handles. (See Douglas Gilbert's
+ description of the SG driver for details:
+ http://www.torque.net/sg/p/scsi-generic_long.txt)
+
+ For this reason, sanei_scsi_open does not allow to open
+ two or more file handles simultaneously.
+ */
+ if (0 == ioctl(fd, SG_GET_RESERVED_SIZE, &real_buffersize))
+ {
+ /* if we got more memory than requested, we stick with
+ with the requested value, in order to allow
+ sanei_scsi_open to check the buffer size exactly.
+ */
+ if (real_buffersize > *buffersize)
+ {
+ sanei_scsi_max_request_size = *buffersize;
+ }
+ else
+ {
+ sanei_scsi_max_request_size = real_buffersize;
+ *buffersize = real_buffersize;
+ }
+ }
+ else
+ {
+ DBG(1, "sanei_scsi_open: cannot read SG buffer size - %s\n",
+ strerror(errno));
+ close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG(1, "sanei_scsi_open_extended: using %i bytes as SCSI buffer\n",
+ sanei_scsi_max_request_size);
+
+ if (sg_version >= 20135)
+ {
+ DBG(1, "trying to enable low level command queueing\n");
+
+ if (0 == ioctl(fd, SG_GET_SCSI_ID, &sid))
+ {
+ DBG(1, "sanei_scsi_open: Host adapter queue depth: %i\n",
+ sid.d_queue_depth);
+
+ ioctl_val = 1;
+ if(0 == ioctl(fd, SG_SET_COMMAND_Q, &ioctl_val))
+ {
+ fdpa->sg_queue_max = sid.d_queue_depth;
+ if (fdpa->sg_queue_max <= 0)
+ fdpa->sg_queue_max = 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* we have the old SG driver: */
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ }
+ if (fdpa->sg_queue_max > 1)
+ DBG(1, "sanei_scsi_open: low level command queueing enabled\n");
+ }
+#endif /* LINUX_INTERFACE */
#endif /* !DECUNIX_INTERFACE */

if (fd >= num_alloced)
@@ -958,9 +1145,78 @@
return SANE_STATUS_GOOD;
}

+#if USE == LINUX_INTERFACE
+/* The "wrapper" for the old open call */
+SANE_Status
+sanei_scsi_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ int i, j = 0;
+ int wanted_buffersize = SCSIBUFFERSIZE, real_buffersize;
+ SANE_Status res;
+ char *cc, *cc1;
+
+ cc = getenv("SANE_SG_BUFFERSIZE");
+ if (cc)
+ {
+ i = strtol(cc, &cc1, 10);
+ if (cc != cc1 && i >= 32768)
+ wanted_buffersize = i;
+ }
+
+ real_buffersize = wanted_buffersize;
+ res = sanei_scsi_open_extended(dev, fdp, handler, handler_arg,
+ &real_buffersize);
+
+ /* make sure that we got as much memory as we wanted, otherwise
+ the backend might be confused
+ */
+ if (real_buffersize != wanted_buffersize)
+ {
+ DBG(1, "sanei_scsi_open: could not allocate SG buffer memory "
+ "wanted: %i got: %i\n", wanted_buffersize, real_buffersize);
+ sanei_scsi_close(*fdp);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return res;
+}
+#else
+/* dummy for the proposed new open call */
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+{
+ SANE_Status res;
+ res = sanei_scsi_open(dev, fdp, handler, handler_arg);
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ return res;
+}
+#endif
+
void
sanei_scsi_close (int fd)
{
+#if USE == LINUX_INTERFACE
+ if (fd_info[fd].pdata)
+ {
+ req *req, *next_req;
+
+ /* make sure that there are no pending SCSI calls */
+ sanei_scsi_req_flush_all_extended(fd);
+
+ req = ((fdparms*) fd_info[fd].pdata)->sane_free_list;
+ while (req)
+ {
+ next_req = req->next;
+ free(req);
+ req = next_req;
+ }
+ free(fd_info[fd].pdata);
+ }
+#endif
+
fd_info[fd].in_use = 0;
fd_info[fd].sense_handler = 0;
fd_info[fd].sense_handler_arg = 0;
@@ -1225,69 +1481,133 @@
} \
while (0)

-static struct req
- {
- struct req *next;
- int fd;
- u_int running:1, done:1;
- SANE_Status status;
- size_t *dst_len;
- void *dst;
- struct
- {
- struct sg_header hdr;
- /* Make sure this is the last element, the real size is
- SG_BIG_BUFF and machine dependant */
- u_int8_t data[1];
- }
- cdb;
- }
-*qhead, *qtail, *free_list;
-
static void
issue (struct req *req)
{
ssize_t nwritten;
+ fdparms *fdp;
+ struct req *rp;
+ int retries;

- if (!req || req->running)
+ if (!req)
return;
-
+ fdp = (fdparms*) fd_info[req->fd].pdata;
DBG (4, "sanei_scsi.issue: %p\n", req);

- ATOMIC (req->running = 1;
- nwritten = write (req->fd, &req->cdb, req->cdb.hdr.pack_len));
-
- if (nwritten != req->cdb.hdr.pack_len)
- {
- DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
- strerror (errno));
- req->done = 1;
- if (errno == ENOMEM)
- {
- DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
- "Check file PROBLEMS.\n");
- req->status = SANE_STATUS_NO_MEM;
- }
+ rp = fdp->sane_qhead;
+ while (rp && rp->running)
+ rp = rp->next;
+
+ while (rp && fdp->sg_queue_used < fdp->sg_queue_max)
+ {
+ retries = 20;
+ while (retries)
+ {
+ ATOMIC (rp->running = 1;
+ nwritten = write (rp->fd, &rp->cdb, rp->cdb.hdr.pack_len);
+ if (nwritten != rp->cdb.hdr.pack_len)
+ {
+ /* ENOMEM can easily happen, if both command queueing
+ inside the SG driver and large buffers are used.
+ Therefore, if ENOMEM does not occur for the first
+ command in the queue, we can simply try to issue
+ it later again.
+ */
+ if ( errno == EAGAIN
+ || (errno == ENOMEM && rp != fdp->sane_qhead))
+ {
+ /* don't try to send the data again, but
+ wait for the next call to issue()
+ */
+ rp->running = 0;
+ }
+ }
+ );
+ if (rp == fdp->sane_qhead && errno == EAGAIN)
+ {
+ retries--;
+ usleep(10000);
+ }
+ else
+ retries = 0;
+ }
+
+ if (nwritten != rp->cdb.hdr.pack_len)
+ {
+ if (rp->running)
+ {
+ DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
+ strerror (errno));
+ rp->done = 1;
+ if (errno == ENOMEM)
+ {
+ DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
+ "Check file PROBLEMS.\n");
+ rp->status = SANE_STATUS_NO_MEM;
+ }
+ else
+ rp->status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ if (errno == ENOMEM)
+ DBG(1, "issue: ENOMEM - cannot queue SCSI command. "
+ "Trying again later.\n");
+ else
+ DBG(1, "issue: cannot queue SCSI command. "
+ "Trying again later.\n");
+ }
+ break; /* in case of an error don't try to queue more commands */
+ }
else
- req->status = SANE_STATUS_IO_ERROR;
+ fdp->sg_queue_used++;
+ rp = rp->next;
}
}

void
-sanei_scsi_req_flush_all (void)
+sanei_scsi_req_flush_all_extended (int fd)
{
+ fdparms *fdp;
struct req *req, *next_req;

- for (req = qhead; req; req = next_req)
+ fdp = (fdparms*) fd_info[fd].pdata;
+ for (req = fdp->sane_qhead; req; req = next_req)
{
if (req->running && !req->done)
- read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
+ {
+ read (fd, &req->cdb, req->cdb.hdr.reply_len);
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;
+ }
next_req = req->next;

- req->next = free_list;
- free_list = req;
+ req->next = fdp->sane_free_list;
+ fdp->sane_free_list = req;
}
- qhead = qtail = 0;
+ fdp->sane_qhead = fdp->sane_qtail = 0;
+}
+
+void
+sanei_scsi_req_flush_all ()
+{
+ int fd, i, j = 0;
+
+ /* sanei_scsi_open allows only one open file handle, so we
+ can simply look for the first entry where in_use is set
+ */
+ +
+ fd = num_alloced;
+ for (i = 0; i < num_alloced; i++)
+ if (fd_info[i].in_use)
+ {
+ j++;
+ fd = i;
+ }
+
+ assert(j < 2);
+
+ if (fd < num_alloced)
+ sanei_scsi_req_flush_all_extended(fd);
}

SANE_Status
@@ -1296,11 +1616,14 @@
{
struct req *req;
size_t size;
+ fdparms *fdp;
+
+ fdp = (fdparms*) fd_info[fd].pdata;

- if (free_list)
+ if (fdp->sane_free_list)
{
- req = free_list;
- free_list = req->next;
+ req = fdp->sane_free_list;
+ fdp->sane_free_list = req->next;
req->next = 0;
}
else
@@ -1328,17 +1651,23 @@
memcpy (&req->cdb.data, src, src_size);

req->next = 0;
- ATOMIC (if (qtail)
+ ATOMIC (if (fdp->sane_qtail)
{
- qtail->next = req;
- qtail = req;
+ fdp->sane_qtail->next = req;
+ fdp->sane_qtail = req;
}
else
- qhead = qtail = req);
+ fdp->sane_qhead = fdp->sane_qtail = req);

DBG (4, "scsi_req_enter: entered %p\n", req);

*idp = req;
+ issue(req);
+
+ DBG(10, "scsi_req_enter: queue_used: %i, queue_max: %i\n",
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_used,
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_max);
+
return SANE_STATUS_GOOD;
}

@@ -1349,7 +1678,8 @@
struct req *req = id;
ssize_t nread = 0;

- assert (req == qhead); /* we don't support out-of-order completion */
+ /* we don't support out-of-order completion */
+ assert (req == ((fdparms*)fd_info[req->fd].pdata)->sane_qhead);

DBG (4, "sanei_scsi_req_wait: waiting for %p\n", req);

@@ -1372,6 +1702,9 @@
ATOMIC (nread = read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
req->done = 1);

+ if (fd_info[req->fd].pdata)
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;
+
/* Now issue next command asap, if any. We can't do this
earlier since the Linux kernel has space for just one big
buffer. */
@@ -1423,11 +1756,12 @@
}

/* dequeue and release processed request: */
- ATOMIC (qhead = qhead->next;
- if (!qhead)
- qtail = 0;
- req->next = free_list;
- free_list = req);
+ ATOMIC (((fdparms*) fd_info[req->fd].pdata)->sane_qhead
+ = ((fdparms*) fd_info[req->fd].pdata)->sane_qhead->next;
+ if (!((fdparms*) fd_info[req->fd].pdata)->sane_qhead)
+ ((fdparms*) fd_info[req->fd].pdata)->sane_qtail = 0;
+ req->next = ((fdparms*) fd_info[req->fd].pdata)->sane_free_list;
+ ((fdparms*) fd_info[req->fd].pdata)->sane_free_list = req);
return status;
}

--- sane-1.0.1-orig/include/sane/sanei_scsi.h Thu May 14 08:08:22 1998
+++ sane-1.0.1/include/sane/sanei_scsi.h Sat Sep 25 18:25:24 1999
@@ -48,6 +48,31 @@
SANEI_SCSI_Sense_Handler sense_handler,
void *sense_arg);

+/* The extended open call allows a backend to ask for a specific
+ buffer size. sanei_scsi_open tries to allocate a buffer of the
+ size given by *buffersize upon entry to this function. If
+ sanei_scsi_open_extended returns successfully, *buffersize
+ contains the available buffer size. This value may be both
+ smaller or larger than the value requested by the backend;
+ it can even be zero. The backend must decide, if it got enough
+ buffer memory to work.
+
+ Note that the value of *buffersize may differ for different
+ files.
+*/
+extern SANE_Status sanei_scsi_open_extended (
+ const char * device_name, int * fd,
+ SANEI_SCSI_Sense_Handler sense_handler,
+ void *sense_arg, int *buffersize);
+
+/* Let backends decide, which open call to use:
+ if HAVE_SANEI_SCSI_OPEN_EXTENDED is defined, sanei_scsi_open_extended
+ may be used.
+ May also be used to decide, if sanei_scsi_req_flush_all or
+ sanei_scsi_req_flush_all_extended should be used.
+*/
+#define HAVE_SANEI_SCSI_OPEN_EXTENDED
+
/* One or more scsi commands can be enqueued by calling req_enter().
SRC is the pointer to the SCSI command and associated write data
and SRC_SIZE is the length of the command and data. DST is a
@@ -76,8 +101,13 @@
const void * src, size_t src_size,
void * dst, size_t * dst_size);

-/* Flush all pending SCSI commands. */
+/* Flush all pending SCSI commands. This function work only,
+ if zero or one SCSI file handles are open.
+*/
extern void sanei_scsi_req_flush_all (void);
+
+/* Flush all SCSI commands pending for one handle */
+extern void sanei_scsi_req_flush_all_extended (int fd);

extern void sanei_scsi_close (int fd);

--- sane-1.0.1-orig/configure.in Sun Apr 4 00:44:56 1999
+++ sane-1.0.1/configure.in Mon Sep 20 20:41:44 1999
@@ -192,10 +192,19 @@
else
DLL_PRELOAD=""
fi
+
+AC_ARG_ENABLE(scsibuffersize,
+ [ --enable-scsibuffersize=N
+ specify the default size of the buffer for SCSI commands],
+ [set_scsibuffersize="$enableval"], [set_scsibuffersize=131072])
+CFLAGS="$CFLAGS -DSCSIBUFFERSIZE=$set_scsibuffersize"
+echo "scsi buffersize: $set_scsibuffersize"
+
AC_SUBST(V_MAJOR)
AC_SUBST(V_MINOR)
AC_SUBST(V_REV)
AC_SUBST(DLL_PRELOAD)
+

AC_OUTPUT([Makefile lib/Makefile sanei/Makefile frontend/Makefile
japi/Makefile backend/Makefile include/Makefile doc/Makefile

--------------5D2267F9E78A0A1B29D131D6--

--
Source code, list archive, and docs: http://www.mostang.com/sane/
To unsubscribe: echo unsubscribe sane-devel | mail majordomo@mostang.com