O_NDELAY open in FreeBSD tape driver
Matt Jacob
mjacob at FreeBSD.org
Sun Jan 8 11:20:54 PST 2006
The Bacula folks convinced me to try a bit harder to emulate Linux and Solaris
tape open behaviour which allows an open to succeed with no tape iff the
mode is O_NONBLOCK.
The patch below seems to do the trick- and still preserves the FreeBSD
semantics in that an O_NONBLOCK open with an samount that fails leaves
the tape 'open', but 'open pending mount' so that attempts to actually
*do* anything with a tape retries the samount (returning an error if
failed, otherwise transitioning to full open state). An open without
O_NONBLOCK works just as it did before.
At the same time, I also threw in a check for rdonly opens so that we don't
allow a filemark to be written to a tape that was opened O_RDONLY.
The advantage, other than similarity to Linux or Solaris, is that a pending
open is useful for backup packages that open the tape and then wait for the
robotics to catch up and insert that tape. Personally, I would have just
kept retrying the open until it succeeded, but it *is* true that a number
of packages do it the O_NONBLOCK way.
I checked the FreeBSD source tree and amanda, and I didn't see them using
O_NONBLOCK, so this change shouldn't affect them.
Comments?
-matt
Index: scsi_sa.c
===================================================================
RCS file: /home/ncvs/src/sys/cam/scsi/scsi_sa.c,v
retrieving revision 1.105
diff -u -r1.105 scsi_sa.c
--- scsi_sa.c 1 Jul 2005 15:21:30 -0000 1.105
+++ scsi_sa.c 8 Jan 2006 19:08:02 -0000
@@ -44,6 +44,7 @@
#ifdef _KERNEL
#include <sys/conf.h>
#endif
+#include <sys/fcntl.h>
#include <sys/devicestat.h>
#ifndef _KERNEL
@@ -255,8 +256,10 @@
* Misc other flags/state
*/
u_int32_t
- : 31,
- ctrl_mode : 1; /* control device open */
+ : 29,
+ open_rdonly : 1, /* open read-only */
+ open_pending_mount : 1, /* open pending mount */
+ ctrl_mode : 1; /* control device open */
};
struct sa_quirk_entry {
@@ -468,23 +471,37 @@
cam_periph_unlock(periph);
return (ENXIO);
}
+
if (SA_IS_CTRL(dev)) {
softc->ctrl_mode = 1;
cam_periph_unlock(periph);
return (0);
}
-
if (softc->flags & SA_FLAG_OPEN) {
error = EBUSY;
} else if (softc->flags & SA_FLAG_INVALID) {
error = ENXIO;
} else {
/*
+ * Preserve whether this is a read_only open.
+ */
+ softc->open_rdonly = (flags & O_RDWR) == O_RDONLY;
+
+ /*
* The function samount ensures media is loaded and ready.
* It also does a device RESERVE if the tape isn't yet mounted.
+ *
+ * If the mount fails and this was a non-blocking open,
+ * make this a 'open_pending_mount' action.
*/
error = samount(periph, flags, dev);
+ if (error && (flags & O_NONBLOCK)) {
+ softc->flags |= SA_FLAG_OPEN;
+ softc->open_pending_mount = 1;
+ cam_periph_unlock(periph);
+ return (0);
+ }
}
if (error) {
@@ -521,6 +538,7 @@
return (error);
}
+ softc->open_rdonly = 0;
if (SA_IS_CTRL(dev)) {
softc->ctrl_mode = 0;
cam_periph_release(periph);
@@ -528,6 +546,14 @@
return (0);
}
+ if (softc->open_pending_mount) {
+ softc->flags &= ~SA_FLAG_OPEN;
+ softc->open_pending_mount = 0;
+ cam_periph_release(periph);
+ cam_periph_unlock(periph);
+ return (0);
+ }
+
/*
* Were we writing the tape?
*/
@@ -681,10 +707,32 @@
return;
}
+ /*
+ * This should actually never occur as the write(2)
+ * system call traps attempts to write to a read-only
+ * file descriptor.
+ */
+ if (bp->bio_cmd == BIO_WRITE && softc->open_rdonly) {
+ splx(s);
+ biofinish(bp, NULL, EBADF);
+ return;
+ }
+
splx(s);
+ if (softc->open_pending_mount) {
+ int error = samount(periph, 0, bp->bio_dev);
+ if (error) {
+ biofinish(bp, NULL, ENXIO);
+ return;
+ }
+ saprevent(periph, PR_PREVENT);
+ softc->open_pending_mount = 0;
+ }
+
+
/*
- * If it's a null transfer, return immediatly
+ * If it's a null transfer, return immediately
*/
if (bp->bio_bcount == 0) {
biodone(bp);
@@ -756,6 +804,17 @@
return;
}
+
+#define PENDING_MOUNT_CHECK(softc, periph, dev) \
+ if (softc->open_pending_mount) { \
+ error = samount(periph, 0, dev); \
+ if (error) { \
+ break; \
+ } \
+ saprevent(periph, PR_PREVENT); \
+ softc->open_pending_mount = 0; \
+ }
+
static int
saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
{
@@ -865,7 +924,7 @@
* If this isn't the control mode device, actually go out
* and ask the drive again what it's set to.
*/
- if (!SA_IS_CTRL(dev)) {
+ if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) {
u_int8_t write_protect;
int comp_enabled, comp_supported;
error = sagetparams(periph, SA_PARAM_ALL,
@@ -962,7 +1021,8 @@
bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
sizeof (sep->ctl_cdb));
- if (SA_IS_CTRL(dev) == 0 || didlockperiph)
+ if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) ||
+ didlockperiph)
bzero((caddr_t) &softc->errinfo,
sizeof (softc->errinfo));
error = 0;
@@ -973,8 +1033,11 @@
struct mtop *mt;
int count;
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
mt = (struct mtop *)arg;
+
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
("saioctl: op=0x%x count=0x%x\n",
mt->mt_op, mt->mt_count));
@@ -1067,6 +1130,7 @@
break;
}
case MTREW: /* rewind */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
(void) sacheckeod(periph);
error = sarewind(periph);
/* see above */
@@ -1076,12 +1140,14 @@
softc->filemarks = 0;
break;
case MTERASE: /* erase */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = saerase(periph, count);
softc->flags &=
~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
softc->flags &= ~SA_FLAG_ERR_PENDING;
break;
case MTRETENS: /* re-tension tape */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = saretension(periph);
softc->flags &=
~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
@@ -1089,6 +1155,8 @@
break;
case MTOFFL: /* rewind and put the drive offline */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
(void) sacheckeod(periph);
/* see above */
softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
@@ -1119,6 +1187,8 @@
case MTSETBSIZ: /* Set block size for device */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
0, 0, 0);
if (error == 0) {
@@ -1161,6 +1231,8 @@
}
break;
case MTSETDNSTY: /* Set density for device and mode */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
if (count > UCHAR_MAX) {
error = EINVAL;
break;
@@ -1170,6 +1242,7 @@
}
break;
case MTCOMP: /* enable compression */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
/*
* Some devices don't support compression, and
* don't like it if you ask them for the
@@ -1193,15 +1266,19 @@
error = 0;
break;
case MTIOCRDSPOS:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sardpos(periph, 0, (u_int32_t *) arg);
break;
case MTIOCRDHPOS:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sardpos(periph, 1, (u_int32_t *) arg);
break;
case MTIOCSLOCATE:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sasetpos(periph, 0, (u_int32_t *) arg);
break;
case MTIOCHLOCATE:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sasetpos(periph, 1, (u_int32_t *) arg);
break;
case MTIOCGETEOTMODEL:
@@ -3147,6 +3224,8 @@
int error, nwm = 0;
softc = (struct sa_softc *)periph->softc;
+ if (softc->open_rdonly)
+ return (EBADF);
ccb = cam_periph_getccb(periph, 1);
/*
@@ -3364,6 +3443,8 @@
int error;
softc = (struct sa_softc *)periph->softc;
+ if (softc->open_rdonly)
+ return (EBADF);
ccb = cam_periph_getccb(periph, 1);
More information about the freebsd-scsi
mailing list