scsi_da.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,638 行 · 第 1/3 页

C
1,638
字号
/* * Implementation of SCSI Direct Access Peripheral driver for CAM. * * Copyright (c) 1997 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions, and the following disclaimer, *    without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *      $Id: scsi_da.c,v 1.19.2.4 1999/05/09 01:27:39 ken Exp $ */#include "opt_hw_wdog.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/buf.h>#include <sys/devicestat.h>#include <sys/dkbad.h>#include <sys/disklabel.h>#include <sys/diskslice.h>#include <sys/malloc.h>#include <sys/conf.h>#include <machine/cons.h>#include <machine/md_var.h>#include <vm/vm.h>#include <vm/vm_prot.h>#include <vm/pmap.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_extend.h>#include <cam/cam_periph.h>#include <cam/cam_xpt_periph.h>#include <cam/scsi/scsi_message.h>typedef enum {	DA_STATE_PROBE,	DA_STATE_NORMAL} da_state;typedef enum {	DA_FLAG_PACK_INVALID	= 0x001,	DA_FLAG_NEW_PACK	= 0x002,	DA_FLAG_PACK_LOCKED	= 0x004,	DA_FLAG_PACK_REMOVABLE	= 0x008,	DA_FLAG_TAGGED_QUEUING	= 0x010,	DA_FLAG_NEED_OTAG	= 0x020,	DA_FLAG_WENT_IDLE	= 0x040,	DA_FLAG_RETRY_UA	= 0x080,	DA_FLAG_OPEN		= 0x100} da_flags;typedef enum {	DA_Q_NONE		= 0x00,	DA_Q_NO_SYNC_CACHE	= 0x01,	DA_Q_NO_6_BYTE		= 0x02} da_quirks;typedef enum {	DA_CCB_PROBE		= 0x01,	DA_CCB_BUFFER_IO	= 0x02,	DA_CCB_WAITING		= 0x03,	DA_CCB_DUMP		= 0x04,	DA_CCB_TYPE_MASK	= 0x0F,	DA_CCB_RETRY_UA		= 0x10} da_ccb_state;/* Offsets into our private area for storing information */#define ccb_state	ppriv_field0#define ccb_bp		ppriv_ptr1struct disk_params {	u_int8_t  heads;	u_int16_t cylinders;	u_int8_t  secs_per_track;	u_int32_t secsize;	/* Number of bytes/sector */	u_int32_t sectors;	/* total number sectors */};struct da_softc {	struct	 buf_queue_head buf_queue;	struct	 devstat device_stats;	SLIST_ENTRY(da_softc) links;	LIST_HEAD(, ccb_hdr) pending_ccbs;	da_state state;	da_flags flags;		da_quirks quirks;	int	 minimum_cmd_size;	int	 ordered_tag_count;	struct	 disk_params params;	struct	 diskslices *dk_slices;	/* virtual drives */	union	 ccb saved_ccb;};struct da_quirk_entry {	struct scsi_inquiry_pattern inq_pat;	da_quirks quirks;};static struct da_quirk_entry da_quirk_table[] ={	{		/*		 * This particular Fujitsu drive doesn't like the		 * synchronize cache command.		 * Reported by: Tom Jackson <toj@gorilla.net>		 */		{T_DIRECT, SIP_MEDIA_FIXED, "FUJITSU", "M2954*", "*"},		/*quirks*/ DA_Q_NO_SYNC_CACHE		},	{		/*		 * This drive doesn't like the synchronize cache command		 * either.  Reported by: Matthew Jacob <mjacob@feral.com>		 * in NetBSD PR kern/6027, August 24, 1998.		 */		{T_DIRECT, SIP_MEDIA_FIXED, "MICROP", "2217*", "*"},		/*quirks*/ DA_Q_NO_SYNC_CACHE	},	{		/*		 * This drive doesn't like the synchronize cache command		 * either.  Reported by: Hellmuth Michaelis (hm@kts.org)		 * (PR 8882).		 */		{T_DIRECT, SIP_MEDIA_FIXED, "MICROP", "2112*", "*"},		/*quirks*/ DA_Q_NO_SYNC_CACHE	},	{		/*		 * Doesn't like the synchronize cache command.		 * Reported by: Blaz Zupan <blaz@gold.amis.net>		 */		{T_DIRECT, SIP_MEDIA_FIXED, "NEC", "D3847*", "*"},		/*quirks*/ DA_Q_NO_SYNC_CACHE	},	{		/*		 * Doesn't work correctly with 6 byte reads/writes.		 * Returns illegal request, and points to byte 9 of the		 * 6-byte CDB.		 * Reported by:  Adam McDougall <bsdx@spawnet.com>		 */		{T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "VIKING 4*", "*"},		/*quirks*/ DA_Q_NO_6_BYTE	},	{		/*		 * See above.		 */		{T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "VIKING 2*", "*"},		/*quirks*/ DA_Q_NO_6_BYTE	}};static	d_open_t	daopen;static	d_read_t	daread;static	d_write_t	dawrite;static	d_close_t	daclose;static	d_strategy_t	dastrategy;static	d_ioctl_t	daioctl;static	d_dump_t	dadump;static	d_psize_t	dasize;static	periph_init_t	dainit;static	void		daasync(void *callback_arg, u_int32_t code,				struct cam_path *path, void *arg);static	periph_ctor_t	daregister;static	periph_dtor_t	dacleanup;static	periph_start_t	dastart;static	periph_oninv_t	daoninvalidate;static	void		dadone(struct cam_periph *periph,			       union ccb *done_ccb);static  int		daerror(union ccb *ccb, u_int32_t cam_flags,				u_int32_t sense_flags);static void		daprevent(struct cam_periph *periph, int action);static void		dasetgeom(struct cam_periph *periph,				  struct scsi_read_capacity_data * rdcap);static timeout_t	dasendorderedtag;static void		dashutdown(int howto, void *arg);#ifndef DA_DEFAULT_TIMEOUT#define DA_DEFAULT_TIMEOUT 60	/* Timeout in seconds */#endif/* * DA_ORDEREDTAG_INTERVAL determines how often, relative * to the default timeout, we check to see whether an ordered * tagged transaction is appropriate to prevent simple tag * starvation.  Since we'd like to ensure that there is at least * 1/2 of the timeout length left for a starved transaction to * complete after we've sent an ordered tag, we must poll at least * four times in every timeout period.  This takes care of the worst * case where a starved transaction starts during an interval that * meets the requirement "don't send an ordered tag" test so it takes * us two intervals to determine that a tag must be sent. */#ifndef DA_ORDEREDTAG_INTERVAL#define DA_ORDEREDTAG_INTERVAL 4#endifstatic struct periph_driver dadriver ={	dainit, "da",	TAILQ_HEAD_INITIALIZER(dadriver.units), /* generation */ 0};DATA_SET(periphdriver_set, dadriver);#define DA_CDEV_MAJOR 13#define DA_BDEV_MAJOR 4/* For 2.2-stable support */#ifndef D_DISK#define D_DISK 0#endifstatic struct cdevsw da_cdevsw = {	/*d_open*/	daopen,	/*d_close*/	daclose,	/*d_read*/	daread,	/*d_write*/	dawrite,	/*d_ioctl*/	daioctl,	/*d_stop*/	nostop,	/*d_reset*/	noreset,	/*d_devtotty*/	nodevtotty,	/*d_poll*/	seltrue,	/*d_mmap*/	nommap,	/*d_strategy*/	dastrategy,	/*d_name*/	"da",	/*d_spare*/	NULL,	/*d_maj*/	-1,	/*d_dump*/	dadump,	/*d_psize*/	dasize,	/*d_flags*/	D_DISK,	/*d_maxio*/	0,	/*b_maj*/	-1};static SLIST_HEAD(,da_softc) softc_list;static struct extend_array *daperiphs;static intdaopen(dev_t dev, int flags, int fmt, struct proc *p){	struct cam_periph *periph;	struct da_softc *softc;	struct disklabel label;		int unit;	int part;	int error;	int s;	unit = dkunit(dev);	part = dkpart(dev);	periph = cam_extend_get(daperiphs, unit);	if (periph == NULL)		return (ENXIO);		softc = (struct da_softc *)periph->softc;	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,	    ("daopen: dev=0x%x (unit %d , partition %d)\n", dev,	     unit, part));	if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {		return (error); /* error code from tsleep */	}	if ((softc->flags & DA_FLAG_OPEN) == 0) {		if (cam_periph_acquire(periph) != CAM_REQ_CMP)			return(ENXIO);		softc->flags |= DA_FLAG_OPEN;	}	s = splsoftcam();	if ((softc->flags & DA_FLAG_PACK_INVALID) != 0) {		/*		 * If any partition is open, although the disk has		 * been invalidated, disallow further opens.		 */		if (dsisopen(softc->dk_slices)) {			splx(s);			cam_periph_unlock(periph);			return (ENXIO);		}		/* Invalidate our pack information. */		dsgone(&softc->dk_slices);		softc->flags &= ~DA_FLAG_PACK_INVALID;	}	splx(s);	/* Do a read capacity */	{		struct scsi_read_capacity_data *rcap;		union  ccb *ccb;		rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),								M_TEMP,								M_WAITOK);				ccb = cam_periph_getccb(periph, /*priority*/1);		scsi_read_capacity(&ccb->csio,				   /*retries*/1,				   /*cbfncp*/dadone,				   MSG_SIMPLE_Q_TAG,				   rcap,				   SSD_FULL_SIZE,				   /*timeout*/60000);		ccb->ccb_h.ccb_bp = NULL;		error = cam_periph_runccb(ccb, daerror, /*cam_flags*/0,					  /*sense_flags*/SF_RETRY_UA |							 SF_RETRY_SELTO,					  &softc->device_stats);		xpt_release_ccb(ccb);		if (error == 0) {			dasetgeom(periph, rcap);		}		free(rcap, M_TEMP);	}	if (error == 0) {		struct ccb_getdev cgd;		/* Build label for whole disk. */		bzero(&label, sizeof(label));		label.d_type = DTYPE_SCSI;		/*		 * Grab the inquiry data to get the vendor and product names.		 * Put them in the typename and packname for the label.		 */		xpt_setup_ccb(&cgd.ccb_h, periph->path, /*priority*/ 1);		cgd.ccb_h.func_code = XPT_GDEV_TYPE;		xpt_action((union ccb *)&cgd);		strncpy(label.d_typename, cgd.inq_data.vendor,			min(SID_VENDOR_SIZE, sizeof(label.d_typename)));		strncpy(label.d_packname, cgd.inq_data.product,			min(SID_PRODUCT_SIZE, sizeof(label.d_packname)));				label.d_secsize = softc->params.secsize;		label.d_nsectors = softc->params.secs_per_track;		label.d_ntracks = softc->params.heads;		label.d_ncylinders = softc->params.cylinders;		label.d_secpercyl = softc->params.heads				  * softc->params.secs_per_track;		label.d_secperunit = softc->params.sectors;		if ((dsisopen(softc->dk_slices) == 0)		    && ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0)) {			daprevent(periph, PR_PREVENT);		}			/* Initialize slice tables. */		error = dsopen("da", dev, fmt, 0, &softc->dk_slices, &label,			       dastrategy, (ds_setgeom_t *)NULL,			       &da_cdevsw);		/*		 * Check to see whether or not the blocksize is set yet.		 * If it isn't, set it and then clear the blocksize		 * unavailable flag for the device statistics.		 */		if ((softc->device_stats.flags & DEVSTAT_BS_UNAVAILABLE) != 0){			softc->device_stats.block_size = softc->params.secsize;			softc->device_stats.flags &= ~DEVSTAT_BS_UNAVAILABLE;		}	}		if (error != 0) {		if ((dsisopen(softc->dk_slices) == 0)		 && ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0)) {			daprevent(periph, PR_ALLOW);		}	}	cam_periph_unlock(periph);	return (error);}static intdaclose(dev_t dev, int flag, int fmt, struct proc *p){	struct	cam_periph *periph;	struct	da_softc *softc;	int	unit;	int	error;	unit = dkunit(dev);	periph = cam_extend_get(daperiphs, unit);	if (periph == NULL)		return (ENXIO);		softc = (struct da_softc *)periph->softc;	if ((error = cam_periph_lock(periph, PRIBIO)) != 0) {		return (error); /* error code from tsleep */	}	dsclose(dev, fmt, softc->dk_slices);	if (dsisopen(softc->dk_slices)) {		cam_periph_unlock(periph);		return (0);	}	if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) {		union	ccb *ccb;		ccb = cam_periph_getccb(periph, /*priority*/1);		scsi_synchronize_cache(&ccb->csio,				       /*retries*/1,				       /*cbfcnp*/dadone,				       MSG_SIMPLE_Q_TAG,				       /*begin_lba*/0,/* Cover the whole disk */				       /*lb_count*/0,				       SSD_FULL_SIZE,				       5 * 60 * 1000);		cam_periph_runccb(ccb, /*error_routine*/NULL, /*cam_flags*/0,				  /*sense_flags*/SF_RETRY_UA,				  &softc->device_stats);		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {			if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==			     CAM_SCSI_STATUS_ERROR) {				int asc, ascq;				int sense_key, error_code;				scsi_extract_sense(&ccb->csio.sense_data,						   &error_code,						   &sense_key, 						   &asc, &ascq);				if (sense_key != SSD_KEY_ILLEGAL_REQUEST)					scsi_sense_print(&ccb->csio);			} else {				xpt_print_path(periph->path);				printf("Synchronize cache failed, status "				       "== 0x%x, scsi status == 0x%x\n",				       ccb->csio.ccb_h.status,				       ccb->csio.scsi_status);			}		}		if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)			cam_release_devq(ccb->ccb_h.path,					 /*relsim_flags*/0,					 /*reduction*/0,					 /*timeout*/0,					 /*getcount_only*/0);		xpt_release_ccb(ccb);	}	if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0) {		daprevent(periph, PR_ALLOW);		/*		 * If we've got removeable media, mark the blocksize as		 * unavailable, since it could change when new media is		 * inserted.		 */		softc->device_stats.flags |= DEVSTAT_BS_UNAVAILABLE;	}	softc->flags &= ~DA_FLAG_OPEN;	cam_periph_unlock(periph);	cam_periph_release(periph);	return (0);	}static intdaread(dev_t dev, struct uio *uio, int ioflag){	return(physio(dastrategy, NULL, dev, 1, minphys, uio));}static intdawrite(dev_t dev, struct uio *uio, int ioflag){	return(physio(dastrategy, NULL, dev, 0, minphys, uio));}/* * 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. */static voiddastrategy(struct buf *bp){	struct cam_periph *periph;	struct da_softc *softc;	u_int  unit;	u_int  part;	int    s;		unit = dkunit(bp->b_dev);	part = dkpart(bp->b_dev);	periph = cam_extend_get(daperiphs, unit);	if (periph == NULL) {		bp->b_error = ENXIO;		goto bad;			}	softc = (struct da_softc *)periph->softc;#if 0	/*	 * check it's not too big a transfer for our adapter	 */	scsi_minphys(bp,&sd_switch);#endif	/*	 * Do bounds checking, adjust transfer, set b_cylin and b_pbklno.	 */	if (dscheck(bp, softc->dk_slices) <= 0)		goto done;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?