Module Name:    src
Committed By:   snj
Date:           Sun Apr  8 06:11:41 UTC 2018

Modified Files:
        src/sys/dev/scsipi [netbsd-8]: st.c stvar.h

Log Message:
Pull up following revision(s) (requested by mlelstv in ticket #703):
        sys/dev/scsipi/st.c: 1.234
        sys/dev/scsipi/stvar.h: 1.26
Use separate lock to protect internal state and release locks when
calling biodone.


To generate a diff of this commit:
cvs rdiff -u -r1.230.8.1 -r1.230.8.2 src/sys/dev/scsipi/st.c
cvs rdiff -u -r1.25 -r1.25.10.1 src/sys/dev/scsipi/stvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/scsipi/st.c
diff -u src/sys/dev/scsipi/st.c:1.230.8.1 src/sys/dev/scsipi/st.c:1.230.8.2
--- src/sys/dev/scsipi/st.c:1.230.8.1	Wed Jun 21 18:18:55 2017
+++ src/sys/dev/scsipi/st.c	Sun Apr  8 06:11:41 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: st.c,v 1.230.8.1 2017/06/21 18:18:55 snj Exp $ */
+/*	$NetBSD: st.c,v 1.230.8.2 2018/04/08 06:11:41 snj Exp $ */
 
 /*-
  * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: st.c,v 1.230.8.1 2017/06/21 18:18:55 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: st.c,v 1.230.8.2 2018/04/08 06:11:41 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_scsi.h"
@@ -343,6 +343,7 @@ static int	st_mount_tape(dev_t, int);
 static void	st_unmount(struct st_softc *, boolean);
 static int	st_decide_mode(struct st_softc *, boolean);
 static void	ststart(struct scsipi_periph *);
+static int	ststart1(struct scsipi_periph *, struct buf *);
 static void	strestart(void *);
 static void	stdone(struct scsipi_xfer *, int);
 static int	st_read(struct st_softc *, char *, int, int);
@@ -392,9 +393,11 @@ stattach(device_t parent, device_t self,
 	/* Set initial flags  */
 	st->flags = ST_INIT_FLAGS;
 
-	/* Set up the buf queue for this device */
+	/* Set up the buf queues for this device */
 	bufq_alloc(&st->buf_queue, "fcfs", 0);
+	bufq_alloc(&st->buf_defer, "fcfs", 0);
 	callout_init(&st->sc_callout, 0);
+	mutex_init(&st->sc_iolock, MUTEX_DEFAULT, IPL_VM);
 
 	/*
 	 * Check if the drive is a known criminal and take
@@ -445,6 +448,7 @@ stdetach(device_t self, int flags)
 	mutex_enter(chan_mtx(chan));
 
 	/* Kill off any queued buffers. */
+	bufq_drain(st->buf_defer);
 	bufq_drain(st->buf_queue);
 
 	/* Kill off any pending commands. */
@@ -452,7 +456,9 @@ stdetach(device_t self, int flags)
 
 	mutex_exit(chan_mtx(chan));
 
+	bufq_free(st->buf_defer);
 	bufq_free(st->buf_queue);
+	mutex_destroy(&st->sc_iolock);
 
 	/* Nuke the vnodes for any open instances */
 	mn = STUNIT(device_unit(self));
@@ -609,6 +615,8 @@ stopen(dev_t dev, int flags, int mode, s
 		 */
 		if ((st->flags & ST_MOUNTED) || ST_MOUNT_DELAY == 0 ||
 		    (st->mt_key != SKEY_NOT_READY)) {
+			device_printf(st->sc_dev, "mount error (key=%d)\n",
+				st->mt_key);
 			goto bad;
 		}
 
@@ -629,11 +637,11 @@ stopen(dev_t dev, int flags, int mode, s
 
 		periph->periph_flags = oflags;	/* restore flags */
 		if (slpintr != 0 && slpintr != EWOULDBLOCK) {
+			device_printf(st->sc_dev, "load interrupted\n");
 			goto bad;
 		}
 	}
 
-
 	/*
 	 * If the mode is 3 (e.g. minor = 3,7,11,15) then the device has
 	 * been opened to set defaults and perform other, usually non-I/O
@@ -642,7 +650,9 @@ stopen(dev_t dev, int flags, int mode, s
 	 * as to whether or not we got a NOT READY for the above
 	 * unit attention). If a tape is there, go do a mount sequence.
 	 */
-	if (stmode == CTRL_MODE && st->mt_key == SKEY_NOT_READY) {
+	if (stmode == CTRL_MODE &&
+	    st->mt_key != SKEY_NO_SENSE &&
+	    st->mt_key != SKEY_UNIT_ATTENTION) {
 		periph->periph_flags |= PERIPH_OPEN;
 		return 0;
 	}
@@ -1107,160 +1117,187 @@ abort:
 
 /*
  * ststart looks to see if there is a buf waiting for the device
- * and that the device is not already busy. If both are true,
- * It dequeues the buf and creates a scsi command to perform the
- * transfer required. The transfer request will call scsipi_done
- * on completion, which will in turn call this routine again
- * so that the next queued transfer is performed.
- * The bufs are queued by the strategy routine (ststrategy)
+ * and that the device is not already busy. If the device is busy,
+ * the request is deferred and retried on the next attempt.
+ * If both are true, ststart creates a scsi command to perform
+ * the transfer required.
+ *
+ * The transfer request will call scsipi_done on completion,
+ * which will in turn call this routine again so that the next
+ * queued transfer is performed. The bufs are queued by the
+ * strategy routine (ststrategy)
  *
  * This routine is also called after other non-queued requests
  * have been made of the scsi driver, to ensure that the queue
  * continues to be drained.
  * ststart() is called with channel lock held
  */
-static void
-ststart(struct scsipi_periph *periph)
+static int
+ststart1(struct scsipi_periph *periph, struct buf *bp)
 {
 	struct st_softc *st = device_private(periph->periph_dev);
-	struct buf *bp;
+        struct scsipi_channel *chan = periph->periph_channel;
 	struct scsi_rw_tape cmd;
 	struct scsipi_xfer *xs;
-	int flags, error __diagused;
+	int flags, error;
 
-	SC_DEBUG(periph, SCSIPI_DB2, ("ststart "));
-	/* See if there is a buf to do and we are not already  doing one */
-	while (periph->periph_active < periph->periph_openings) {
-		/* if a special awaits, let it proceed first */
-		if (periph->periph_flags & PERIPH_WAITING) {
-			periph->periph_flags &= ~PERIPH_WAITING;
-			cv_broadcast(periph_cv_periph(periph));
-			return;
-		}
+	SC_DEBUG(periph, SCSIPI_DB2, ("ststart1 "));
 
-		/*
-		 * If the device has been unmounted by the user
-		 * then throw away all requests until done.
-		 */
-		if (__predict_false((st->flags & ST_MOUNTED) == 0 ||
-		    (periph->periph_flags & PERIPH_MEDIA_LOADED) == 0)) {
-			if ((bp = bufq_get(st->buf_queue)) != NULL) {
-				/* make sure that one implies the other.. */
-				periph->periph_flags &= ~PERIPH_MEDIA_LOADED;
-				bp->b_error = EIO;
-				bp->b_resid = bp->b_bcount;
-				biodone(bp);
-				continue;
-			} else
-				return;
-		}
+	mutex_enter(chan_mtx(chan));
 
-		if ((bp = bufq_peek(st->buf_queue)) == NULL)
-			return;
+	if (periph->periph_active >= periph->periph_openings) {
+		error = EAGAIN;
+		goto out;
+	}
 
-		iostat_busy(st->stats);
+	/* if a special awaits, let it proceed first */
+	if (periph->periph_flags & PERIPH_WAITING) {
+		periph->periph_flags &= ~PERIPH_WAITING;
+		cv_broadcast(periph_cv_periph(periph));
+		error = EAGAIN;
+		goto out;
+	}
+
+	/*
+	 * If the device has been unmounted by the user
+	 * then throw away all requests until done.
+	 */
+	if (__predict_false((st->flags & ST_MOUNTED) == 0 ||
+	    (periph->periph_flags & PERIPH_MEDIA_LOADED) == 0)) {
+		error = EIO;
+		goto out;
+	}
 
+	/*
+	 * only FIXEDBLOCK devices have pending I/O or space operations.
+	 */
+	if (st->flags & ST_FIXEDBLOCKS) {
 		/*
-		 * only FIXEDBLOCK devices have pending I/O or space operations.
+		 * If we are at a filemark but have not reported it yet
+		 * then we should report it now
 		 */
-		if (st->flags & ST_FIXEDBLOCKS) {
-			/*
-			 * If we are at a filemark but have not reported it yet
-			 * then we should report it now
-			 */
-			if (st->flags & ST_AT_FILEMARK) {
-				if ((bp->b_flags & B_READ) == B_WRITE) {
-					/*
-					 * Handling of ST_AT_FILEMARK in
-					 * st_space will fill in the right file
-					 * mark count.
-					 * Back up over filemark
-					 */
-					if (st_space(st, 0, SP_FILEMARKS, 0)) {
-						bufq_get(st->buf_queue);
-						bp->b_error = EIO;
-						bp->b_resid = bp->b_bcount;
-						biodone(bp);
-						continue;
-					}
-				} else {
-					bufq_get(st->buf_queue);
-					bp->b_resid = bp->b_bcount;
-					bp->b_error = 0;
-					st->flags &= ~ST_AT_FILEMARK;
-					biodone(bp);
-					continue;	/* seek more work */
+		if (st->flags & ST_AT_FILEMARK) {
+			if ((bp->b_flags & B_READ) == B_WRITE) {
+				/*
+				 * Handling of ST_AT_FILEMARK in
+				 * st_space will fill in the right file
+				 * mark count.
+				 * Back up over filemark
+				 */
+				if (st_space(st, 0, SP_FILEMARKS, 0)) {
+					error = EIO;
+					goto out;
 				}
+			} else {
+				bp->b_resid = bp->b_bcount;
+				error = 0;
+				st->flags &= ~ST_AT_FILEMARK;
+				goto out;
 			}
 		}
-		/*
-		 * If we are at EOM but have not reported it
-		 * yet then we should report it now.
-		 */
-		if (st->flags & (ST_EOM_PENDING|ST_EIO_PENDING)) {
-			bufq_get(st->buf_queue);
-			bp->b_resid = bp->b_bcount;
-			if (st->flags & ST_EIO_PENDING)
-				bp->b_error = EIO;
-			st->flags &= ~(ST_EOM_PENDING|ST_EIO_PENDING);
-			biodone(bp);
-			continue;	/* seek more work */
-		}
+	}
+	/*
+	 * If we are at EOM but have not reported it
+	 * yet then we should report it now.
+	 */
+	if (st->flags & (ST_EOM_PENDING|ST_EIO_PENDING)) {
+		error = EIO;
+		goto out;
+	}
 
-		/* Fill out the scsi command */
-		memset(&cmd, 0, sizeof(cmd));
-		flags = XS_CTL_NOSLEEP | XS_CTL_ASYNC;
-		if ((bp->b_flags & B_READ) == B_WRITE) {
-			cmd.opcode = WRITE;
-			st->flags &= ~ST_FM_WRITTEN;
-			flags |= XS_CTL_DATA_OUT;
-		} else {
-			cmd.opcode = READ;
-			flags |= XS_CTL_DATA_IN;
-		}
+	/* Fill out the scsi command */
+	memset(&cmd, 0, sizeof(cmd));
+	flags = XS_CTL_NOSLEEP | XS_CTL_ASYNC;
+	if ((bp->b_flags & B_READ) == B_WRITE) {
+		cmd.opcode = WRITE;
+		st->flags &= ~ST_FM_WRITTEN;
+		flags |= XS_CTL_DATA_OUT;
+	} else {
+		cmd.opcode = READ;
+		flags |= XS_CTL_DATA_IN;
+	}
+
+	/*
+	 * Handle "fixed-block-mode" tape drives by using the
+	 * block count instead of the length.
+	 */
+	if (st->flags & ST_FIXEDBLOCKS) {
+		cmd.byte2 |= SRW_FIXED;
+		_lto3b(bp->b_bcount / st->blksize, cmd.len);
+	} else
+		_lto3b(bp->b_bcount, cmd.len);
 
+	/* Clear 'position updated' indicator */
+	st->flags &= ~ST_POSUPDATED;
+
+	/* go ask the adapter to do all this for us */
+	xs = scsipi_make_xs_locked(periph,
+	    (struct scsipi_generic *)&cmd, sizeof(cmd),
+	    (u_char *)bp->b_data, bp->b_bcount,
+	    0, ST_IO_TIME, bp, flags);
+	if (__predict_false(xs == NULL)) {
 		/*
-		 * Handle "fixed-block-mode" tape drives by using the
-		 * block count instead of the length.
+		 * out of memory. Keep this buffer in the queue, and
+		 * retry later.
 		 */
-		if (st->flags & ST_FIXEDBLOCKS) {
-			cmd.byte2 |= SRW_FIXED;
-			_lto3b(bp->b_bcount / st->blksize, cmd.len);
-		} else
-			_lto3b(bp->b_bcount, cmd.len);
+		callout_reset(&st->sc_callout, hz / 2, strestart,
+		    periph);
+		error = EAGAIN;
+		goto out;
+	}
 
-		/* Clear 'position updated' indicator */
-		st->flags &= ~ST_POSUPDATED;
+	error = scsipi_execute_xs(xs);
+	/* with a scsipi_xfer preallocated, scsipi_command can't fail */
+	KASSERT(error == 0);
 
-		/* go ask the adapter to do all this for us */
-		xs = scsipi_make_xs_locked(periph,
-		    (struct scsipi_generic *)&cmd, sizeof(cmd),
-		    (u_char *)bp->b_data, bp->b_bcount,
-		    0, ST_IO_TIME, bp, flags);
-		if (__predict_false(xs == NULL)) {
-			/*
-			 * out of memory. Keep this buffer in the queue, and
-			 * retry later.
-			 */
-			callout_reset(&st->sc_callout, hz / 2, strestart,
-			    periph);
-			return;
+out:
+	mutex_exit(chan_mtx(chan));
+
+	return error;
+}
+
+static void
+ststart(struct scsipi_periph *periph)
+{
+	struct st_softc *st = device_private(periph->periph_dev);
+        struct scsipi_channel *chan = periph->periph_channel;
+	struct buf *bp;
+	int error;
+
+	SC_DEBUG(periph, SCSIPI_DB2, ("ststart "));
+
+	mutex_exit(chan_mtx(chan));
+	mutex_enter(&st->sc_iolock);
+
+	while ((bp = bufq_get(st->buf_defer)) != NULL
+	       || (bp = bufq_get(st->buf_queue)) != NULL) {
+
+		iostat_busy(st->stats);
+		mutex_exit(&st->sc_iolock);
+
+		error = ststart1(periph, bp);
+
+		mutex_enter(&st->sc_iolock);
+		if (error != 0)
+			iostat_unbusy(st->stats, 0,
+			              ((bp->b_flags & B_READ) == B_READ));
+		if (error == EAGAIN) {
+			bufq_put(st->buf_defer, bp);
+			break;
 		}
-		/*
-		 * need to dequeue the buffer before queuing the command,
-		 * because cdstart may be called recursively from the
-		 * HBA driver
-		 */
-#ifdef DIAGNOSTIC
-		if (bufq_get(st->buf_queue) != bp)
-			panic("ststart(): dequeued wrong buf");
-#else
-		bufq_get(st->buf_queue);
-#endif
-		error = scsipi_execute_xs(xs);
-		/* with a scsipi_xfer preallocated, scsipi_command can't fail */
-		KASSERT(error == 0);
-	} /* go back and see if we can cram more work in.. */
+		mutex_exit(&st->sc_iolock);
+
+		if (error != 0) {
+			bp->b_error = error;
+			bp->b_resid = bp->b_bcount;
+			biodone(bp);
+		}
+
+		mutex_enter(&st->sc_iolock);
+	}
+
+	mutex_exit(&st->sc_iolock);
+	mutex_enter(chan_mtx(chan));
 }
 
 static void
@@ -1291,6 +1328,8 @@ stdone(struct scsipi_xfer *xs, int error
 		if (bp->b_resid > bp->b_bcount || bp->b_resid < 0)
 			bp->b_resid = bp->b_bcount;
 
+		mutex_enter(&st->sc_iolock);
+
 		if ((bp->b_flags & B_READ) == B_WRITE)
 			st->flags |= ST_WRITTEN;
 		else
@@ -1299,8 +1338,6 @@ stdone(struct scsipi_xfer *xs, int error
 		iostat_unbusy(st->stats, bp->b_bcount,
 			     ((bp->b_flags & B_READ) == B_READ));
 
-		rnd_add_uint32(&st->rnd_source, bp->b_blkno);
-
 		if ((st->flags & ST_POSUPDATED) == 0) {
 			if (error) {
 				st->fileno = st->blkno = -1;
@@ -1312,6 +1349,11 @@ stdone(struct scsipi_xfer *xs, int error
 					st->blkno++;
 			}
 		}
+
+		mutex_exit(&st->sc_iolock);
+
+		rnd_add_uint32(&st->rnd_source, bp->b_blkno);
+
 		biodone(bp);
 	}
 }

Index: src/sys/dev/scsipi/stvar.h
diff -u src/sys/dev/scsipi/stvar.h:1.25 src/sys/dev/scsipi/stvar.h:1.25.10.1
--- src/sys/dev/scsipi/stvar.h:1.25	Mon Apr 13 16:33:25 2015
+++ src/sys/dev/scsipi/stvar.h	Sun Apr  8 06:11:41 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: stvar.h,v 1.25 2015/04/13 16:33:25 riastradh Exp $ */
+/*	$NetBSD: stvar.h,v 1.25.10.1 2018/04/08 06:11:41 snj Exp $ */
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -138,12 +138,15 @@ struct st_softc {
 						 */
 	struct bufq_state *buf_queue;	/* the queue of pending IO */
 					/* operations */
+	struct bufq_state *buf_defer;	/* the queue of deferred IO */
+					/* operations */
 	struct callout sc_callout;	/* restarting the queue after */
 					/* transient error */
 
 	struct io_stats *stats;		/* statistics for the drive */
 
 	krndsource_t	rnd_source;
+	kmutex_t	sc_iolock;
 };
 
 #define	ST_INFO_VALID	0x0001

Reply via email to