Re: SG_BIG_BUFF, glibc 2.1 weirdness ...

abel deuring (a.deuring@satzbau-gmbh.de)
Sun, 22 Aug 1999 21:44:22 +0200

Hi all!

Andreas Beck wrote:

> I'll take care for a graceful handling of the condition as well as support
> for the new sg driver in 2.2.6 and up.

Sorry for the delay with my response to Andreas' mail, but I was
on holidays the last weeks.

I have already made some work on the support for the new Linux
SG driver in sanei_scsi.c, in order to use the capabilites of
this driver to change its buffer size dynamically, and to enable
low level command queueing.

Some weeks ago, I already sent some patches on the same
topic to this list, but they were not that reliable
regarding the command queueing. Thanks to Douglas Gilbert,
who helped me understanding several details of the SG driver
and even modified an ioctl call, so that it is possible
to read the queue depth of a SCSI adapter (or its low level
driver).

Two details of the modifications would need an improvement:

1. It would be fine to have the buffer size user
configurable. This could be done either with a configuration
file, or with an environment variable.

2. The new SG driver allocates a separate buffer for each
file handle. This means, that a situation is possible, where
the ioctl call to modify the buffer size is successful for
one file handle, but it fails for another. This leads to an
inconsistency with sanei_max_scsi_request_size. This
inconsistency can only be avoided, if
sanei_max_scsi_request_size would become an array, or if the
buffer size would be stored in an additional field in
fd_info. Since this would require modifications to most if
not all backends, and since this inconsistency only occurs
if a frontend tries to attach to two or more scanners at the
same time - which none the existing frontends does - I left
this inconsistency for now. It should of course be fixed, if
these patches will be accepted.

Abel

--- sanei_scsi.c-orig Sat Jul 3 16:59:46 1999
+++ sanei_scsi.c Sun Aug 22 20:02:22 1999
@@ -194,6 +194,52 @@
#endif

int sanei_scsi_max_request_size = MAX_DATA;
+#if USE == LINUX_INTERFACE
+/* to following #defines follow Doug Gilbert's sample code
+ to achieve 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
+
+/* 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
+ version 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 Fdparms
+ {
+ int queue_used;
+ int queue_max;
+ }
+fdparms;
+
+#endif

#if USE == FREEBSD_CAM_INTERFACE
# define CAM_MAXDEVS 128
@@ -621,6 +667,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 +960,86 @@
}
}
#endif /* SGIOCSTL */
+#if USE == LINUX_INTERFACE
+ {
+ SG_scsi_id sid;
+ int ioctl_val, sg_version;
+ fdparms *fdpa = 0;
+
+ /* check for 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 version 21.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", ioctl_val);
+
+ /* try to reserve a bigger SG buffer. 128 kB should be OK
+ for most situations.
+ xxxxxxx
+ NOTE: sanei_scsi_max_request_size is a global variable
+ used for all devices/file handles, while the new
+ SG driver allocates buffer memory for each opened
+ file separately. Therefore, we have a possible
+ inconsistency, if more than one file is opened and
+ if the SG_SET_RESERVED_SIZE call fails for one
+ of these calls. This needs to be cleaned up!!
+ (ideas: check, if another file has already been
+ opened; if so, check for matching values
+ of SG_GET_RESERVED_SIZE. OTOH, this affects at
+ present a very theoretical situtation, that a
+ frontend accesses more than one scanner.
+
+ A better solution would be to add a field to struct
+ fd_parms which stores the actual buffer size for each
+ file handle. But to use this requires modifications
+ in many if not all backends....
+
+ */
+ ioctl_val = 128 * 1024;
+ ioctl(fd, SG_SET_RESERVED_SIZE, &ioctl_val);
+
+ if (0 == ioctl(fd, SG_GET_RESERVED_SIZE, &ioctl_val))
+ sanei_scsi_max_request_size = ioctl_val;
+ else
+ DBG(1, "sanei_scsi_open: cannot read SG buffer size - %s\n",
+ strerror(errno));
+ DBG(1, "sanei_scsi_open: 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))
+ {
+ pdata = fdpa = malloc(sizeof(fdparms));
+ if (!pdata)
+ {
+ close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ fdpa->queue_used = 0;
+ fdpa->queue_max = sid.d_queue_depth;
+ if (fdpa->queue_max <= 0)
+ fdpa->queue_max = 1;
+ }
+ }
+ }
+ }
+ if (fdpa)
+ DBG(1, "sanei_scsi_open: low level command queueing enabled\n");
+ }
+#endif /* LINUX_INTERFACE */
#endif /* !DECUNIX_INTERFACE */

if (fd >= num_alloced)
@@ -967,6 +1094,10 @@
if (!fd_info[fd].fake_fd)
close (fd);

+#if USE == LINUX_INTERFACE
+ if (fd_info[fd].pdata)
+ free(fd_info[fd].pdata);
+#endif
#if USE == FREEBSD_CAM_INTERFACE
cam_close_device(cam_devices[fd]);
cam_devices[fd] = NULL;
@@ -1247,16 +1378,44 @@
static void
issue (struct req *req)
{
- ssize_t nwritten;
+ ssize_t nwritten, count = 0;
+ fdparms *fdp;

if (!req || req->running)
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 (!fdp)
+ {
+ ATOMIC (req->running = 1;
+ nwritten = write (req->fd, &req->cdb,
req->cdb.hdr.pack_len));
+ }
+ else
+ {
+ DBG(2, "sanei_scsi.issue: queue used: %i, queue max: %i\n",
+ fdp->queue_used, fdp->queue_max);
+ assert(fdp->queue_used < fdp->queue_max);
+ while (count < 100)
+ {
+ ATOMIC (nwritten = write (req->fd, &req->cdb,
req->cdb.hdr.pack_len);
+ if (nwritten !=req->cdb.hdr.pack_len
+ && errno == EAGAIN
+ && count < 100)
+ count++;
+ else
+ req->running = 1;
+ );
+ if (!req->running)
+ {
+ usleep(10000);
+ DBG(2, "sanei_scsi.issue: write call returned EAGAIN\n");
+ }
+ else
+ break;
+ }
+ }
if (nwritten != req->cdb.hdr.pack_len)
{
DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
@@ -1271,6 +1430,9 @@
else
req->status = SANE_STATUS_IO_ERROR;
}
+ else
+ if (fdp)
+ fdp->queue_used++;
}

void
@@ -1281,7 +1443,11 @@
for (req = qhead; req; req = next_req)
{
if (req->running && !req->done)
- read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
+ {
+ read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
+ if (fd_info[req->fd].pdata)
+ ((fdparms*) fd_info[req->fd].pdata)->queue_used--;
+ }
next_req = req->next;

req->next = free_list;
@@ -1339,6 +1505,10 @@
DBG (4, "scsi_req_enter: entered %p\n", req);

*idp = req;
+ if ( fd_info[fd].pdata
+ && ((fdparms*) fd_info[req->fd].pdata)->queue_used
+ < ((fdparms*) fd_info[req->fd].pdata)->queue_max)
+ issue(req);
return SANE_STATUS_GOOD;
}

@@ -1369,9 +1539,15 @@
select (req->fd + 1, &readable, 0, 0, 0);

/* now atomically read result and set DONE: */
+ DBG (4, "sanei_scsi_req_wait: reading...\n"); /* xxx */
ATOMIC (nread = read (req->fd, &req->cdb,
req->cdb.hdr.reply_len);
req->done = 1);
+ DBG (4, "sanei_scsi_req_wait: reading ok\n"); /* xxx */
+
+ if (fd_info[req->fd].pdata)
+ ((fdparms*) fd_info[req->fd].pdata)->queue_used--;

+ DBG (4, "sanei_scsi_req_wait: reading ok2 %p\n", req->next); /*
xxx */
/* Now issue next command asap, if any. We can't do this
earlier since the Linux kernel has space for just one big
buffer. */

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