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 + -
显示快捷键?