scsi_target.c

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

C
1,732
字号
/* * Implementation of a simple Target Mode SCSI Proccessor Target driver for CAM. * * Copyright (c) 1998, 1999 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_target.c,v 1.7.2.3 1999/05/04 19:25:53 gibbs Exp $ */#include <stddef.h>	/* For offsetof */#include <sys/param.h>#include <sys/queue.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/types.h>#include <sys/buf.h>#include <sys/conf.h>#include <sys/devicestat.h>#include <sys/malloc.h>#include <sys/poll.h>#include <sys/select.h>	/* For struct selinfo. */#include <sys/uio.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_extend.h>#include <cam/cam_periph.h>#include <cam/cam_queue.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_pt.h>#include <cam/scsi/scsi_targetio.h>#include <cam/scsi/scsi_message.h>typedef enum {	TARG_STATE_NORMAL,	TARG_STATE_EXCEPTION,	TARG_STATE_TEARDOWN} targ_state;typedef enum {	TARG_FLAG_NONE		 = 0x00,	TARG_FLAG_SEND_EOF	 = 0x01,	TARG_FLAG_RECEIVE_EOF	 = 0x02,	TARG_FLAG_LUN_ENABLED	 = 0x04} targ_flags;typedef enum {	TARG_CCB_WORKQ,	TARG_CCB_WAITING} targ_ccb_types;#define MAX_ACCEPT	16#define MAX_IMMEDIATE	16#define MAX_BUF_SIZE	256	/* Max inquiry/sense/mode page transfer */#define MAX_INITIATORS	16	/* XXX More for Fibre-Channel */#define MIN(a, b) ((a > b) ? b : a)#define TARG_CONTROL_UNIT 0xffff00ff#define TARG_IS_CONTROL_DEV(unit) ((unit) == TARG_CONTROL_UNIT)/* Offsets into our private CCB area for storing accept information */#define ccb_type	ppriv_field0#define ccb_descr	ppriv_ptr1/* We stick a pointer to the originating accept TIO in each continue I/O CCB */#define ccb_atio	ppriv_ptr1TAILQ_HEAD(ccb_queue, ccb_hdr);struct targ_softc {	struct		ccb_queue pending_queue;	struct		ccb_queue work_queue;	struct		ccb_queue snd_ccb_queue;	struct		ccb_queue rcv_ccb_queue;	struct		ccb_queue unknown_atio_queue;	struct		buf_queue_head snd_buf_queue;	struct		buf_queue_head rcv_buf_queue;	struct		devstat device_stats;	struct		selinfo snd_select;	struct		selinfo rcv_select;	targ_state	state;	targ_flags	flags;		targ_exception	exceptions;		u_int		init_level;	u_int		inq_data_len;	struct		scsi_inquiry_data *inq_data;	struct		ccb_accept_tio *accept_tio_list;	struct		ccb_hdr_slist immed_notify_slist;	struct		initiator_state istate[MAX_INITIATORS];};struct targ_cmd_desc {	struct	  ccb_accept_tio* atio_link;	u_int	  data_resid;	/* How much left to transfer */	u_int	  data_increment;/* Amount to send before next disconnect */	void*	  data;		/* The data. Can be from backing_store or not */	void*	  backing_store;/* Backing store allocated for this descriptor*/	struct	  buf *bp;	/* Buffer for this transfer */	u_int	  max_size;	/* Size of backing_store */	u_int32_t timeout;		u_int8_t  status;	/* Status to return to initiator */};static	d_open_t	targopen;static	d_close_t	targclose;static	d_read_t	targread;static	d_write_t	targwrite;static	d_ioctl_t	targioctl;static	d_poll_t	targpoll;static	d_strategy_t	targstrategy;#define TARG_CDEV_MAJOR	65static struct cdevsw targ_cdevsw = {	/*d_open*/	targopen,	/*d_close*/	targclose,	/*d_read*/	targread,	/*d_write*/	targwrite,	/*d_ioctl*/	targioctl,	/*d_stop*/	nostop,	/*d_reset*/	noreset,	/*d_devtotty*/	nodevtotty,	/*d_poll*/	targpoll,	/*d_mmap*/	nommap,	/*d_strategy*/	targstrategy,	/*d_name*/	"targ",	/*d_spare*/	NULL,	/*d_maj*/	-1,	/*d_dump*/	nodump,	/*d_psize*/	nopsize,	/*d_flags*/	0,	/*d_maxio*/	0,	/*b_maj*/	-1};static int		targsendccb(struct cam_periph *periph, union ccb *ccb,				    union ccb *inccb);static periph_init_t	targinit;static void		targasync(void *callback_arg, u_int32_t code,				struct cam_path *path, void *arg);static int		targallocinstance(struct ioc_alloc_unit *alloc_unit);static int		targfreeinstance(struct ioc_alloc_unit *alloc_unit);static cam_status	targenlun(struct cam_periph *periph);static cam_status	targdislun(struct cam_periph *periph);static periph_ctor_t	targctor;static periph_dtor_t	targdtor;static void		targrunqueue(struct cam_periph *periph,				     struct targ_softc *softc);static periph_start_t	targstart;static void		targdone(struct cam_periph *periph,				 union ccb *done_ccb);static void		targfireexception(struct cam_periph *periph,					  struct targ_softc *softc);static  int		targerror(union ccb *ccb, u_int32_t cam_flags,				  u_int32_t sense_flags);static struct targ_cmd_desc*	allocdescr(void);static void		freedescr(struct targ_cmd_desc *buf);static void		fill_sense(struct scsi_sense_data *sense,				   u_int error_code, u_int sense_key,				   u_int asc, u_int ascq);					static struct periph_driver targdriver ={	targinit, "targ",	TAILQ_HEAD_INITIALIZER(targdriver.units), /* generation */ 0};DATA_SET(periphdriver_set, targdriver);static struct extend_array *targperiphs;static voidtarginit(void){	dev_t dev;	/*	 * Create our extend array for storing the devices we attach to.	 */	targperiphs = cam_extend_new();	if (targperiphs == NULL) {		printf("targ: Failed to alloc extend array!\n");		return;	}	/* If we were successfull, register our devsw */	dev = makedev(TARG_CDEV_MAJOR, 0);	cdevsw_add(&dev,&targ_cdevsw, NULL);}static voidtargasync(void *callback_arg, u_int32_t code,	  struct cam_path *path, void *arg){	struct cam_periph *periph;	periph = (struct cam_periph *)callback_arg;	switch (code) {	case AC_PATH_DEREGISTERED:	{		/* XXX Implement */		break;	}	case AC_BUS_RESET:	{		/* Flush transaction queue */	}	default:		break;	}}/* Attempt to enable our lun */static cam_statustargenlun(struct cam_periph *periph){	union ccb immed_ccb;	struct targ_softc *softc;	cam_status status;	int i;	softc = (struct targ_softc *)periph->softc;	if ((softc->flags & TARG_FLAG_LUN_ENABLED) != 0)		return (CAM_REQ_CMP);	xpt_setup_ccb(&immed_ccb.ccb_h, periph->path, /*priority*/1);	immed_ccb.ccb_h.func_code = XPT_EN_LUN;	/* Don't need support for any vendor specific commands */	immed_ccb.cel.grp6_len = 0;	immed_ccb.cel.grp7_len = 0;	immed_ccb.cel.enable = 1;	xpt_action(&immed_ccb);	status = immed_ccb.ccb_h.status;	if (status != CAM_REQ_CMP) {		xpt_print_path(periph->path);		printf("targenlun - Enable Lun Rejected for status 0x%x\n",		       status);		return (status);	}		softc->flags |= TARG_FLAG_LUN_ENABLED;	/*	 * Build up a buffer of accept target I/O	 * operations for incoming selections.	 */	for (i = 0; i < MAX_ACCEPT; i++) {		struct ccb_accept_tio *atio;		atio = (struct ccb_accept_tio*)malloc(sizeof(*atio), M_DEVBUF,						      M_NOWAIT);		if (atio == NULL) {			status = CAM_RESRC_UNAVAIL;			break;		}		atio->ccb_h.ccb_descr = allocdescr();		if (atio->ccb_h.ccb_descr == NULL) {			free(atio, M_DEVBUF);			status = CAM_RESRC_UNAVAIL;			break;		}		xpt_setup_ccb(&atio->ccb_h, periph->path, /*priority*/1);		atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO;		atio->ccb_h.cbfcnp = targdone;		xpt_action((union ccb *)atio);		status = atio->ccb_h.status;		if (status != CAM_REQ_INPROG) {			xpt_print_path(periph->path);			printf("Queue of atio failed\n");			freedescr(atio->ccb_h.ccb_descr);			free(atio, M_DEVBUF);			break;		}		((struct targ_cmd_desc*)atio->ccb_h.ccb_descr)->atio_link =		    softc->accept_tio_list;		softc->accept_tio_list = atio;	}	if (i == 0) {		xpt_print_path(periph->path);		printf("targenlun - Could not allocate accept tio CCBs: "		       "status = 0x%x\n", status);		targdislun(periph);		return (CAM_REQ_CMP_ERR);	}	/*	 * Build up a buffer of immediate notify CCBs	 * so the SIM can tell us of asynchronous target mode events.	 */	for (i = 0; i < MAX_ACCEPT; i++) {		struct ccb_immed_notify *inot;		inot = (struct ccb_immed_notify*)malloc(sizeof(*inot), M_DEVBUF,						        M_NOWAIT);		if (inot == NULL) {			status = CAM_RESRC_UNAVAIL;			break;		}		xpt_setup_ccb(&inot->ccb_h, periph->path, /*priority*/1);		inot->ccb_h.func_code = XPT_IMMED_NOTIFY;		inot->ccb_h.cbfcnp = targdone;		xpt_action((union ccb *)inot);		status = inot->ccb_h.status;		if (status != CAM_REQ_INPROG) {			printf("Queue of inot failed\n");			free(inot, M_DEVBUF);			break;		}		SLIST_INSERT_HEAD(&softc->immed_notify_slist, &inot->ccb_h,				  periph_links.sle);	}	if (i == 0) {		xpt_print_path(periph->path);		printf("targenlun - Could not allocate immediate notify CCBs: "		       "status = 0x%x\n", status);		targdislun(periph);		return (CAM_REQ_CMP_ERR);	}	return (CAM_REQ_CMP);}static cam_statustargdislun(struct cam_periph *periph){	union ccb ccb;	struct targ_softc *softc;	struct ccb_accept_tio* atio;	struct ccb_hdr *ccb_h;	softc = (struct targ_softc *)periph->softc;	if ((softc->flags & TARG_FLAG_LUN_ENABLED) == 0)		return CAM_REQ_CMP;	/* XXX Block for Continue I/O completion */	/* Kill off all ACCECPT and IMMEDIATE CCBs */	while ((atio = softc->accept_tio_list) != NULL) {		softc->accept_tio_list =		    ((struct targ_cmd_desc*)atio->ccb_h.ccb_descr)->atio_link;		xpt_setup_ccb(&ccb.cab.ccb_h, periph->path, /*priority*/1);		ccb.cab.ccb_h.func_code = XPT_ABORT;		ccb.cab.abort_ccb = (union ccb *)atio;		xpt_action(&ccb);	}	while ((ccb_h = SLIST_FIRST(&softc->immed_notify_slist)) != NULL) {		SLIST_REMOVE_HEAD(&softc->immed_notify_slist, periph_links.sle);		xpt_setup_ccb(&ccb.cab.ccb_h, periph->path, /*priority*/1);		ccb.cab.ccb_h.func_code = XPT_ABORT;		ccb.cab.abort_ccb = (union ccb *)ccb_h;		xpt_action(&ccb);	}	/*	 * Dissable this lun.	 */	xpt_setup_ccb(&ccb.cel.ccb_h, periph->path, /*priority*/1);	ccb.cel.ccb_h.func_code = XPT_EN_LUN;	ccb.cel.enable = 0;	xpt_action(&ccb);	if (ccb.cel.ccb_h.status != CAM_REQ_CMP)		printf("targdislun - Disabling lun on controller failed "		       "with status 0x%x\n", ccb.cel.ccb_h.status);	else 		softc->flags &= ~TARG_FLAG_LUN_ENABLED;	return (ccb.cel.ccb_h.status);}static cam_statustargctor(struct cam_periph *periph, void *arg){	struct ccb_pathinq *cpi;	struct targ_softc *softc;	int i;	cpi = (struct ccb_pathinq *)arg;	/* Allocate our per-instance private storage */	softc = (struct targ_softc *)malloc(sizeof(*softc), M_DEVBUF, M_NOWAIT);	if (softc == NULL) {		printf("targctor: unable to malloc softc\n");		return (CAM_REQ_CMP_ERR);	}	bzero(softc, sizeof(softc));	TAILQ_INIT(&softc->pending_queue);	TAILQ_INIT(&softc->work_queue);	TAILQ_INIT(&softc->snd_ccb_queue);	TAILQ_INIT(&softc->rcv_ccb_queue);	TAILQ_INIT(&softc->unknown_atio_queue);	bufq_init(&softc->snd_buf_queue);	bufq_init(&softc->rcv_buf_queue);	softc->accept_tio_list = NULL;	SLIST_INIT(&softc->immed_notify_slist);	softc->state = TARG_STATE_NORMAL;	periph->softc = softc;	softc->init_level++;	cam_extend_set(targperiphs, periph->unit_number, periph);	/*	 * We start out life with a UA to indicate power-on/reset.	 */	for (i = 0; i < MAX_INITIATORS; i++)		softc->istate[i].pending_ua = UA_POWER_ON;		/*	 * Allocate an initial inquiry data buffer.  We might allow the	 * user to override this later via an ioctl.	 */	softc->inq_data_len = sizeof(*softc->inq_data);	softc->inq_data = malloc(softc->inq_data_len, M_DEVBUF, M_NOWAIT);	if (softc->inq_data == NULL) {		printf("targctor - Unable to malloc inquiry data\n");		targdtor(periph);		return (CAM_RESRC_UNAVAIL);	}	bzero(softc->inq_data, softc->inq_data_len);	softc->inq_data->device = T_PROCESSOR | (SID_QUAL_LU_CONNECTED << 5);	softc->inq_data->version = 2;	softc->inq_data->response_format = 2; /* SCSI2 Inquiry Format */	softc->inq_data->flags =	    cpi->hba_inquiry & (PI_SDTR_ABLE|PI_WIDE_16|PI_WIDE_32);	softc->inq_data->additional_length = softc->inq_data_len - 4;	strncpy(softc->inq_data->vendor, "FreeBSD ", SID_VENDOR_SIZE);	strncpy(softc->inq_data->product, "TM-PT           ", SID_PRODUCT_SIZE);	strncpy(softc->inq_data->revision, "0.0 ", SID_REVISION_SIZE);	softc->init_level++;	return (CAM_REQ_CMP);}static voidtargdtor(struct cam_periph *periph){	struct targ_softc *softc;	softc = (struct targ_softc *)periph->softc;	softc->state = TARG_STATE_TEARDOWN;	targdislun(periph);	cam_extend_release(targperiphs, periph->unit_number);	switch (softc->init_level) {	default:		/* FALLTHROUGH */	case 2:		free(softc->inq_data, M_DEVBUF);		/* FALLTHROUGH */	case 1:		free(softc, M_DEVBUF);		break;	case 0:		panic("targdtor - impossible init level");;	}}static inttargopen(dev_t dev, int flags, int fmt, struct proc *p){	struct cam_periph *periph;	struct	targ_softc *softc;	u_int unit;	cam_status status;	int error;	int s;	unit = minor(dev);	/* An open of the control device always succeeds */	if (TARG_IS_CONTROL_DEV(unit))		return 0;	s = splsoftcam();	periph = cam_extend_get(targperiphs, unit);	if (periph == NULL) {		return (ENXIO);        	splx(s);	}	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {		splx(s);		return (error);	}	softc = (struct targ_softc *)periph->softc;	if ((softc->flags & TARG_FLAG_LUN_ENABLED) == 0) {		if (cam_periph_acquire(periph) != CAM_REQ_CMP) {			splx(s);			cam_periph_unlock(periph);			return(ENXIO);		}	}        splx(s);		status = targenlun(periph);	switch (status) {	case CAM_REQ_CMP:		error = 0;		break;	case CAM_RESRC_UNAVAIL:		error = ENOMEM;		break;	case CAM_LUN_ALRDY_ENA:		error = EADDRINUSE;		break;	default:		error = ENXIO;		break;	}        cam_periph_unlock(periph);	return (error);}static inttargclose(dev_t dev, int flag, int fmt, struct proc *p){	struct	cam_periph *periph;	struct	targ_softc *softc;	u_int	unit;	int	s;	int	error;	unit = minor(dev);	/* A close of the control device always succeeds */	if (TARG_IS_CONTROL_DEV(unit))		return 0;	s = splsoftcam();	periph = cam_extend_get(targperiphs, unit);	if (periph == NULL) {		splx(s);		return (ENXIO);	}	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)		return (error);	softc = (struct targ_softc *)periph->softc;	splx(s);	targdislun(periph);

⌨️ 快捷键说明

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