Esempio n. 1
0
void
sdminphys(struct buf *bp)
{
	struct sd_softc *sc;
	long max;

	sc = sdlookup(DISKUNIT(bp->b_dev));
	if (sc == NULL)
		return;  /* XXX - right way to fail this? */

	/*
	 * If the device is ancient, we want to make sure that
	 * the transfer fits into a 6-byte cdb.
	 *
	 * XXX Note that the SCSI-I spec says that 256-block transfers
	 * are allowed in a 6-byte read/write, and are specified
	 * by setting the "length" to 0.  However, we're conservative
	 * here, allowing only 255-block transfers in case an
	 * ancient device gets confused by length == 0.  A length of 0
	 * in a 10-byte read/write actually means 0 blocks.
	 */
	if (sc->flags & SDF_ANCIENT) {
		max = sc->sc_dk.dk_label->d_secsize * 0xff;

		if (bp->b_bcount > max)
			bp->b_bcount = max;
	}

	(*sc->sc_link->adapter->scsi_minphys)(bp, sc->sc_link);

	device_unref(&sc->sc_dev);
}
Esempio n. 2
0
/*
 * Actually translate the requested transfer into one the physical driver
 * can understand.  The transfer is described by a buf and will include
 * only one physical transfer.
 */
void
sdstrategy(struct buf *bp)
{
	struct sd_softc *sc;
	int s;

	sc = sdlookup(DISKUNIT(bp->b_dev));
	if (sc == NULL) {
		bp->b_error = ENXIO;
		goto bad;
	}
	if (sc->flags & SDF_DYING) {
		bp->b_error = ENXIO;
		goto bad;
	}

	SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
	    bp->b_bcount, (long long)bp->b_blkno));
	/*
	 * If the device has been made invalid, error out
	 */
	if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
		if (sc->sc_link->flags & SDEV_OPEN)
			bp->b_error = EIO;
		else
			bp->b_error = ENODEV;
		goto bad;
	}

	/* Validate the request. */
	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
		goto done;

	/* Place it in the queue of disk activities for this disk. */
	bufq_queue(&sc->sc_bufq, bp);

	/*
	 * Tell the device to get going on the transfer if it's
	 * not doing anything, otherwise just wait for completion
	 */
	scsi_xsh_add(&sc->sc_xsh);

	device_unref(&sc->sc_dev);
	return;

 bad:
	bp->b_flags |= B_ERROR;
	bp->b_resid = bp->b_bcount;
 done:
	s = splbio();
	biodone(bp);
	splx(s);
	if (sc != NULL)
		device_unref(&sc->sc_dev);
}
Esempio n. 3
0
/*
 * Close the device. Only called if we are the last occurrence of an open
 * device.  Convenient now but usually a pain.
 */
int
sdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
    struct sd_softc *sc;
    int part = DISKPART(dev);
    int error;

    sc = sdlookup(DISKUNIT(dev));
    if (sc == NULL)
        return (ENXIO);
    if (sc->flags & SDF_DYING) {
        device_unref(&sc->sc_dev);
        return (ENXIO);
    }

    if ((error = sdlock(sc)) != 0) {
        device_unref(&sc->sc_dev);
        return (error);
    }

    switch (fmt) {
    case S_IFCHR:
        sc->sc_dk.dk_copenmask &= ~(1 << part);
        break;
    case S_IFBLK:
        sc->sc_dk.dk_bopenmask &= ~(1 << part);
        break;
    }
    sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;

    if (sc->sc_dk.dk_openmask == 0) {
        if ((sc->flags & SDF_DIRTY) != 0)
            sd_flush(sc, 0);

        if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
            scsi_prevent(sc->sc_link, PR_ALLOW,
                         SCSI_IGNORE_ILLEGAL_REQUEST |
                         SCSI_IGNORE_NOT_READY | SCSI_SILENT);
        sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);

        if (sc->sc_link->flags & SDEV_EJECTING) {
            scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
            sc->sc_link->flags &= ~SDEV_EJECTING;
        }

        timeout_del(&sc->sc_timeout);
    }

    sdunlock(sc);
    device_unref(&sc->sc_dev);
    return 0;
}
Esempio n. 4
0
/*
 * Close the device. Only called if we are the last occurrence of an open
 * device.  Convenient now but usually a pain.
 */
int
sdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct sd_softc *sc;
	int part = DISKPART(dev);

	sc = sdlookup(DISKUNIT(dev));
	if (sc == NULL)
		return (ENXIO);
	if (sc->flags & SDF_DYING) {
		device_unref(&sc->sc_dev);
		return (ENXIO);
	}

	disk_lock_nointr(&sc->sc_dk);

	disk_closepart(&sc->sc_dk, part, fmt);

	if (sc->sc_dk.dk_openmask == 0) {
		if ((sc->flags & SDF_DIRTY) != 0)
			sd_flush(sc, 0);

		if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
			scsi_prevent(sc->sc_link, PR_ALLOW,
			    SCSI_IGNORE_ILLEGAL_REQUEST |
			    SCSI_IGNORE_NOT_READY | SCSI_SILENT);
		sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);

		if (sc->sc_link->flags & SDEV_EJECTING) {
			scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
			sc->sc_link->flags &= ~SDEV_EJECTING;
		}

		timeout_del(&sc->sc_timeout);
		scsi_xsh_del(&sc->sc_xsh);
	}

	disk_unlock(&sc->sc_dk);
	device_unref(&sc->sc_dev);
	return 0;
}
Esempio n. 5
0
daddr_t
sdsize(dev_t dev)
{
	struct disklabel *lp;
	struct sd_softc *sc;
	int part, omask;
	daddr_t size;

	sc = sdlookup(DISKUNIT(dev));
	if (sc == NULL)
		return -1;
	if (sc->flags & SDF_DYING) {
		size = -1;
		goto exit;
	}

	part = DISKPART(dev);
	omask = sc->sc_dk.dk_openmask & (1 << part);

	if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0) {
		size = -1;
		goto exit;
	}

	lp = sc->sc_dk.dk_label;
	if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
		size = -1;
	else if (lp->d_partitions[part].p_fstype != FS_SWAP)
		size = -1;
	else
		size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
	if (omask == 0 && sdclose(dev, 0, S_IFBLK, NULL) != 0)
		size = -1;

 exit:
	device_unref(&sc->sc_dev);
	return size;
}
Esempio n. 6
0
/*
 * Perform special action on behalf of the user
 * Knows about the internals of this device
 */
int
sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
	struct sd_softc *sc;
	struct disklabel *lp;
	int error = 0;
	int part = DISKPART(dev);

	sc = sdlookup(DISKUNIT(dev));
	if (sc == NULL)
		return (ENXIO);
	if (sc->flags & SDF_DYING) {
		device_unref(&sc->sc_dev);
		return (ENXIO);
	}

	SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));

	/*
	 * If the device is not valid.. abandon ship
	 */
	if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
		switch (cmd) {
		case DIOCLOCK:
		case DIOCEJECT:
		case SCIOCIDENTIFY:
		case SCIOCCOMMAND:
		case SCIOCDEBUG:
			if (part == RAW_PART)
				break;
		/* FALLTHROUGH */
		default:
			if ((sc->sc_link->flags & SDEV_OPEN) == 0) {
				error = ENODEV;
				goto exit;
			} else {
				error = EIO;
				goto exit;
			}
		}
	}

	switch (cmd) {
	case DIOCRLDINFO:
		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
		sdgetdisklabel(dev, sc, lp, 0);
		bcopy(lp, sc->sc_dk.dk_label, sizeof(*lp));
		free(lp, M_TEMP);
		goto exit;

	case DIOCGPDINFO:
		sdgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
		goto exit;

	case DIOCGDINFO:
		*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
		goto exit;

	case DIOCGPART:
		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
		((struct partinfo *)addr)->part =
		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
		goto exit;

	case DIOCWDINFO:
	case DIOCSDINFO:
		if ((flag & FWRITE) == 0) {
			error = EBADF;
			goto exit;
		}

		if ((error = disk_lock(&sc->sc_dk)) != 0)
			goto exit;

		error = setdisklabel(sc->sc_dk.dk_label,
		    (struct disklabel *)addr, sc->sc_dk.dk_openmask);
		if (error == 0) {
			if (cmd == DIOCWDINFO)
				error = writedisklabel(DISKLABELDEV(dev),
				    sdstrategy, sc->sc_dk.dk_label);
		}

		disk_unlock(&sc->sc_dk);
		goto exit;

	case DIOCLOCK:
		error = scsi_prevent(sc->sc_link,
		    (*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
		goto exit;

	case MTIOCTOP:
		if (((struct mtop *)addr)->mt_op != MTOFFL) {
			error = EIO;
			goto exit;
		}
		/* FALLTHROUGH */
	case DIOCEJECT:
		if ((sc->sc_link->flags & SDEV_REMOVABLE) == 0) {
			error = ENOTTY;
			goto exit;
		}
		sc->sc_link->flags |= SDEV_EJECTING;
		goto exit;

	case DIOCINQ:
		error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
		if (error == ENOTTY)
			error = sd_ioctl_inquiry(sc,
			    (struct dk_inquiry *)addr);
		goto exit;

	case DIOCSCACHE:
		if (!ISSET(flag, FWRITE)) {
			error = EBADF;
			goto exit;
		}
		/* FALLTHROUGH */
	case DIOCGCACHE:
		error = sd_ioctl_cache(sc, cmd, (struct dk_cache *)addr);
		goto exit;

	case DIOCCACHESYNC:
		if (!ISSET(flag, FWRITE)) {
			error = EBADF;
			goto exit;
		}
		if ((sc->flags & SDF_DIRTY) != 0 || *(int *)addr != 0)
			error = sd_flush(sc, 0);
		return (error);

	default:
		if (part != RAW_PART) {
			error = ENOTTY;
			goto exit;
		}
		error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
	}

 exit:
	device_unref(&sc->sc_dev);
	return (error);
}
Esempio n. 7
0
/*
 * Open the device. Make sure the partition info is as up-to-date as can be.
 */
int
sdopen(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct scsi_link *sc_link;
	struct sd_softc *sc;
	int error = 0, part, rawopen, unit;

	unit = DISKUNIT(dev);
	part = DISKPART(dev);

	rawopen = (part == RAW_PART) && (fmt == S_IFCHR);

	sc = sdlookup(unit);
	if (sc == NULL)
		return (ENXIO);
	sc_link = sc->sc_link;

	if (sc->flags & SDF_DYING) {
		device_unref(&sc->sc_dev);
		return (ENXIO);
	}
	if (ISSET(flag, FWRITE) && ISSET(sc_link->flags, SDEV_READONLY)) {
		device_unref(&sc->sc_dev);
		return (EACCES);
	}

	SC_DEBUG(sc_link, SDEV_DB1,
	    ("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
	    sd_cd.cd_ndevs, part));

	if ((error = disk_lock(&sc->sc_dk)) != 0) {
		device_unref(&sc->sc_dev);
		return (error);
	}

	if (sc->sc_dk.dk_openmask != 0) {
		/*
		 * If any partition is open, but the disk has been invalidated,
		 * disallow further opens of non-raw partition.
		 */
		if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
			if (rawopen)
				goto out;
			error = EIO;
			goto bad;
		}
	} else {
		/* Spin up non-UMASS devices ready or not. */
		if ((sc->sc_link->flags & SDEV_UMASS) == 0)
			scsi_start(sc_link, SSS_START, (rawopen ? SCSI_SILENT :
			    0) | SCSI_IGNORE_ILLEGAL_REQUEST |
			    SCSI_IGNORE_MEDIA_CHANGE);

		/* Use sd_interpret_sense() for sense errors.
		 *
		 * But only after spinning the disk up! Just in case a broken
		 * device returns "Initialization command required." and causes
		 * a loop of scsi_start() calls.
		 */
		sc_link->flags |= SDEV_OPEN;

		/*
		 * Try to prevent the unloading of a removable device while
		 * it's open. But allow the open to proceed if the device can't
		 * be locked in.
		 */
		if ((sc_link->flags & SDEV_REMOVABLE) != 0) {
			scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT |
			    SCSI_IGNORE_ILLEGAL_REQUEST |
			    SCSI_IGNORE_MEDIA_CHANGE);
		}

		/* Check that it is still responding and ok. */
		error = scsi_test_unit_ready(sc_link,
		    TEST_READY_RETRIES, SCSI_SILENT |
		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);

		if (error) {
			if (rawopen) {
				error = 0;
				goto out;
			} else
				goto bad;
		}

		/* Load the physical device parameters. */
		sc_link->flags |= SDEV_MEDIA_LOADED;
		if (sd_get_parms(sc, &sc->params, (rawopen ? SCSI_SILENT : 0))
		    == SDGP_RESULT_OFFLINE) {
			sc_link->flags &= ~SDEV_MEDIA_LOADED;
			error = ENXIO;
			goto bad;
		}
		SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded\n"));

		/* Load the partition info if not already loaded. */
		if (sdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0) == EIO) {
			error = EIO;
			goto bad;
		}
		SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded\n"));
	}

out:
	if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
		goto bad;

	SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));

	/* It's OK to fall through because dk_openmask is now non-zero. */
bad:
	if (sc->sc_dk.dk_openmask == 0) {
		if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
			scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT |
			    SCSI_IGNORE_ILLEGAL_REQUEST |
			    SCSI_IGNORE_MEDIA_CHANGE);
		sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
	}

	disk_unlock(&sc->sc_dk);
	device_unref(&sc->sc_dev);
	return (error);
}
Esempio n. 8
0
/*
 * Actually translate the requested transfer into one the physical driver
 * can understand.  The transfer is described by a buf and will include
 * only one physical transfer.
 */
void
sdstrategy(struct buf *bp)
{
    struct sd_softc *sc;
    int s;

    sc = sdlookup(DISKUNIT(bp->b_dev));
    if (sc == NULL) {
        bp->b_error = ENXIO;
        goto bad;
    }
    if (sc->flags & SDF_DYING) {
        bp->b_error = ENXIO;
        goto bad;
    }

    SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %d\n",
                                     bp->b_bcount, bp->b_blkno));
    /*
     * If the device has been made invalid, error out
     */
    if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
        if (sc->sc_link->flags & SDEV_OPEN)
            bp->b_error = EIO;
        else
            bp->b_error = ENODEV;
        goto bad;
    }
    /*
     * If it's a null transfer, return immediately
     */
    if (bp->b_bcount == 0)
        goto done;

    /*
     * The transfer must be a whole number of sectors.
     */
    if ((bp->b_bcount % sc->sc_dk.dk_label->d_secsize) != 0) {
        bp->b_error = EINVAL;
        goto bad;
    }
    /*
     * Do bounds checking, adjust transfer. if error, process.
     * If end of partition, just return.
     */
    if (bounds_check_with_label(bp, sc->sc_dk.dk_label,
                                (sc->flags & (SDF_WLABEL|SDF_LABELLING)) != 0) <= 0)
        goto done;

    /*
     * Place it in the queue of disk activities for this disk
     */
    mtx_enter(&sc->sc_buf_mtx);
    disksort(&sc->sc_buf_queue, bp);
    mtx_leave(&sc->sc_buf_mtx);

    /*
     * Tell the device to get going on the transfer if it's
     * not doing anything, otherwise just wait for completion
     */
    sdstart(sc);

    device_unref(&sc->sc_dev);
    return;

bad:
    bp->b_flags |= B_ERROR;
done:
    /*
     * Correctly set the buf to indicate a completed xfer
     */
    bp->b_resid = bp->b_bcount;
    s = splbio();
    biodone(bp);
    splx(s);
    if (sc != NULL)
        device_unref(&sc->sc_dev);
}