diff options
| author | cz147101 <none@none> | 2005-07-17 18:28:49 -0700 |
|---|---|---|
| committer | cz147101 <none@none> | 2005-07-17 18:28:49 -0700 |
| commit | f3531714ff8a7263ce18dd2161d4c64d580dd6d7 (patch) | |
| tree | 9c30e4d660beda1bc6f715cb60501e1bc1bb3f3e /usr | |
| parent | 1e32c0dce17c2f9aeb8cd573e23d417bcecacf88 (diff) | |
| download | illumos-joyent-f3531714ff8a7263ce18dd2161d4c64d580dd6d7.tar.gz | |
6250131 mpt driver fails with large block sizes (64k and above) on x86 platform
6290383 Need to support "dma-max" capability in adpu320
Diffstat (limited to 'usr')
| -rw-r--r-- | usr/src/uts/common/io/scsi/targets/st.c | 349 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/scsi/targets/stdef.h | 40 |
2 files changed, 387 insertions, 2 deletions
diff --git a/usr/src/uts/common/io/scsi/targets/st.c b/usr/src/uts/common/io/scsi/targets/st.c index 7fc2d8c7c3..cbefa10439 100644 --- a/usr/src/uts/common/io/scsi/targets/st.c +++ b/usr/src/uts/common/io/scsi/targets/st.c @@ -41,6 +41,9 @@ #include <sys/file.h> #include <sys/stat.h> #include <sys/kstat.h> +#include <sys/ddidmareq.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> #define IOSP KSTAT_IO_PTR(un->un_stats) /* @@ -84,6 +87,36 @@ extern struct scsi_key_strings scsi_cmds[]; static void *st_state; static char *st_label = "st"; +#if defined(__i386) || defined(__amd64) +/* + * We need to use below DMA attr to alloc physically contiguous + * memory to do I/O in big block size + */ +static ddi_dma_attr_t st_contig_mem_dma_attr = { + DMA_ATTR_V0, /* version number */ + 0x0, /* lowest usable address */ + 0xFFFFFFFFull, /* high DMA address range */ + 0xFFFFFFFFull, /* DMA counter register */ + 1, /* DMA address alignment */ + 1, /* DMA burstsizes */ + 1, /* min effective DMA size */ + 0xFFFFFFFFull, /* max DMA xfer size */ + 0xFFFFFFFFull, /* segment boundary */ + 1, /* s/g list length */ + 1, /* granularity of device */ + 0 /* DMA transfer flags */ +}; + +static ddi_device_acc_attr_t st_acc_attr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC +}; + +/* set limitation for the number of contig_mem */ +static int st_max_contig_mem_num = ST_MAX_CONTIG_MEM_NUM; +#endif + /* * Tunable parameters * @@ -391,6 +424,17 @@ static int st_check_sense_clean_bit(dev_t dev); static int st_clear_unit_attentions(dev_t dev_instance, int max_trys); static void st_calculate_timeouts(struct scsi_tape *un); +#if defined(__i386) || defined(__amd64) +/* + * routines for I/O in big block size + */ +static void st_release_contig_mem(struct scsi_tape *un, struct contig_mem *cp); +static struct contig_mem *st_get_contig_mem(struct scsi_tape *un, size_t len, + int alloc_flags); +static int st_bigblk_xfer_done(struct buf *bp); +static struct buf *st_get_bigblk_bp(struct buf *bp); +#endif + /* * error statistics create/update functions */ @@ -968,7 +1012,11 @@ st_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) } cv_destroy(&un->un_state_cv); - +#if defined(__i386) || defined(__amd64) + if (un->un_contig_mem_hdl != NULL) { + ddi_dma_free_handle(&un->un_contig_mem_hdl); + } +#endif if (un->un_sbufp) { freerbuf(un->un_sbufp); } @@ -1280,6 +1328,9 @@ st_doattach(struct scsi_device *devp, int (*canwait)()) cv_init(&un->un_queue_cv, NULL, CV_DRIVER, NULL); cv_init(&un->un_clscv, NULL, CV_DRIVER, NULL); cv_init(&un->un_state_cv, NULL, CV_DRIVER, NULL); +#if defined(__i386) || defined(__amd64) + cv_init(&un->un_contig_mem_cv, NULL, CV_DRIVER, NULL); +#endif /* Initialize power managemnet condition variable */ cv_init(&un->un_suspend_cv, NULL, CV_DRIVER, NULL); @@ -1299,6 +1350,16 @@ st_doattach(struct scsi_device *devp, int (*canwait)()) un->un_suspend_fileno = 0; un->un_suspend_blkno = 0; +#if defined(__i386) || defined(__amd64) + if (ddi_dma_alloc_handle(ST_DEVINFO, &st_contig_mem_dma_attr, + DDI_DMA_SLEEP, NULL, &un->un_contig_mem_hdl) != DDI_SUCCESS) { + ST_DEBUG6(devp->sd_dev, st_label, SCSI_DEBUG, + "allocation of contiguous memory dma handle failed!"); + un->un_contig_mem_hdl = NULL; + goto error; + } +#endif + /* * Since this driver manages devices with "remote" hardware, * i.e. the devices themselves have no "reg" properties, @@ -1342,6 +1403,11 @@ error: if (un->un_uscsi_rqs_buf) { kmem_free(un->un_uscsi_rqs_buf, SENSE_LENGTH); } +#if defined(__i386) || defined(__amd64) + if (un->un_contig_mem_hdl != NULL) { + ddi_dma_free_handle(&un->un_contig_mem_hdl); + } +#endif ddi_soft_state_free(st_state, instance); devp->sd_private = NULL; } @@ -2329,6 +2395,9 @@ st_close(dev_t dev, int flag, int otyp, cred_t *cred_p) { int err = 0; int norew, count, last_state; +#if defined(__i386) || defined(__amd64) + struct contig_mem *cp, *cp_temp; +#endif GET_SOFT_STATE(dev); @@ -2676,6 +2745,26 @@ error: */ } +#if defined(__i386) || defined(__amd64) + /* + * free any contiguous mem alloc'ed for big block I/O + */ + cp = un->un_contig_mem; + while (cp) { + if (cp->cm_addr) { + ddi_dma_mem_free(&cp->cm_acc_hdl); + } + if (cp->cm_bp) { + freerbuf(cp->cm_bp); + } + cp_temp = cp; + cp = cp->cm_next; + kmem_free(cp_temp, sizeof (struct contig_mem)); + } + un->un_contig_mem_total_num = 0; + un->un_contig_mem_available_num = 0; + un->un_contig_mem = NULL; +#endif ST_DEBUG(ST_DEVINFO, st_label, SCSI_DEBUG, "st_close3: return val = %x, fileno=%x, blkno=%lx, un_eof=%x\n", @@ -3229,7 +3318,17 @@ st_strategy(struct buf *bp) (void *)bp->b_forw, bp->b_bcount, bp->b_resid, bp->b_flags, (void *)BP_PKT(bp)); - +#if defined(__i386) || defined(__amd64) + /* + * We will replace bp with a new bp that can do big blk xfer + * if the requested xfer size is bigger than ST_BIGBLK_XFER + */ + if (bp->b_bcount > ST_BIGBLK_XFER) { + mutex_exit(ST_MUTEX); + bp = st_get_bigblk_bp(bp); + mutex_enter(ST_MUTEX); + } +#endif /* put on wait queue */ ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG, @@ -11362,3 +11461,249 @@ st_calculate_timeouts(struct scsi_tape *un) } } } + +#if defined(__i386) || defined(__amd64) + +/* + * release contig_mem and wake up waiting thread, if any + */ +static void +st_release_contig_mem(struct scsi_tape *un, struct contig_mem *cp) +{ + mutex_enter(ST_MUTEX); + + cp->cm_next = un->un_contig_mem; + un->un_contig_mem = cp; + un->un_contig_mem_available_num++; + cv_broadcast(&un->un_contig_mem_cv); + + mutex_exit(ST_MUTEX); +} + +/* + * St_get_contig_mem will return a contig_mem if there is one available + * in current system. Otherwise, it will try to alloc one, if the total + * number of contig_mem is within st_max_contig_mem_num. + * It will sleep, if allowed by caller or return NULL, if no contig_mem + * is available for now. + */ +static struct contig_mem * +st_get_contig_mem(struct scsi_tape *un, size_t len, int alloc_flags) +{ + size_t rlen; + struct contig_mem *cp = NULL; + ddi_acc_handle_t acc_hdl; + caddr_t addr; + int (*dma_alloc_cb)() = (alloc_flags == KM_SLEEP) ? + DDI_DMA_SLEEP : DDI_DMA_DONTWAIT; + + /* Try to get one available contig_mem */ + mutex_enter(ST_MUTEX); + if (un->un_contig_mem_available_num > 0) { + cp = un->un_contig_mem; + un->un_contig_mem = cp->cm_next; + cp->cm_next = NULL; + un->un_contig_mem_available_num--; + } else if (un->un_contig_mem_total_num < st_max_contig_mem_num) { + /* + * we failed to get one. we're going to + * alloc one more contig_mem for this I/O + */ + mutex_exit(ST_MUTEX); + cp = (struct contig_mem *) + kmem_zalloc(sizeof (struct contig_mem), alloc_flags); + if (cp == NULL) { + ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, + "alloc contig_mem failure\n"); + return (NULL); /* cannot get one */ + } + mutex_enter(ST_MUTEX); + un->un_contig_mem_total_num++; /* one more available */ + } else { + /* + * we failed to get one and we're NOT allowed to + * alloc more contig_mem + */ + if (alloc_flags == KM_SLEEP) { + while (un->un_contig_mem_available_num <= 0) { + cv_wait(&un->un_contig_mem_cv, + ST_MUTEX); + } + cp = un->un_contig_mem; + un->un_contig_mem = cp->cm_next; + cp->cm_next = NULL; + un->un_contig_mem_available_num--; + } else { + mutex_exit(ST_MUTEX); + ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, + "alloc contig_mem failure\n"); + return (NULL); /* cannot get one */ + } + } + mutex_exit(ST_MUTEX); + + /* We need to check if this block of mem is big enough for this I/O */ + if (cp->cm_len < len) { + /* not big enough, need to alloc a new one */ + if (ddi_dma_mem_alloc(un->un_contig_mem_hdl, len, &st_acc_attr, + DDI_DMA_RDWR, dma_alloc_cb, NULL, + &addr, &rlen, &acc_hdl) != DDI_SUCCESS) { + ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, + "alloc contig_mem failure: not enough mem\n"); + st_release_contig_mem(un, cp); + return (NULL); + } + if (cp->cm_addr) { + /* release previous one before we attach new one */ + ddi_dma_mem_free(&cp->cm_acc_hdl); + } + /* attach new mem to this cp */ + cp->cm_addr = addr; + cp->cm_acc_hdl = acc_hdl; + cp->cm_len = len; + + if (cp->cm_bp == NULL) { + cp->cm_bp = getrbuf(alloc_flags); + if (cp->cm_bp == NULL) { + ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, + "alloc contig_mem failure:" + "no enough mem for bp\n"); + st_release_contig_mem(un, cp); + return (NULL); + } + } + } + + /* init bp attached to this cp */ + bioinit(cp->cm_bp); + cp->cm_bp->b_un.b_addr = cp->cm_addr; + cp->cm_bp->b_private = (void *)cp; + return (cp); +} + +/* + * this is the biodone func for the bp used in big block I/O + */ +static int +st_bigblk_xfer_done(struct buf *bp) +{ + struct contig_mem *cp; + struct buf *orig_bp; + int remapped = 0; + int ioerr; + struct scsi_tape *un; + + /* sanity check */ + if (bp == NULL) { + return (DDI_FAILURE); + } + + un = ddi_get_soft_state(st_state, MTUNIT(bp->b_edev)); + if (un == NULL) { + return (DDI_FAILURE); + } + + cp = (struct contig_mem *)bp->b_private; + orig_bp = cp->cm_bp; /* get back the bp we have replaced */ + cp->cm_bp = bp; + + /* special handling for special I/O */ + if (cp->cm_use_sbuf) { + ASSERT(un->un_sbuf_busy); + un->un_sbufp = orig_bp; + cp->cm_use_sbuf = 0; + } + + orig_bp->b_resid = bp->b_resid; + ioerr = geterror(bp); + if (ioerr != 0) { + bioerror(orig_bp, ioerr); + } else if (orig_bp->b_flags & B_READ) { + /* copy data back to original bp */ + if (orig_bp->b_flags & (B_PHYS | B_PAGEIO)) { + bp_mapin(orig_bp); + remapped = 1; + } + bcopy(bp->b_un.b_addr, orig_bp->b_un.b_addr, + bp->b_bcount - bp->b_resid); + if (remapped) + bp_mapout(orig_bp); + } + + st_release_contig_mem(un, cp); + + biodone(orig_bp); + + return (DDI_SUCCESS); +} + +/* + * We use this func to replace original bp that may not be able to do I/O + * in big block size with one that can + */ +static struct buf * +st_get_bigblk_bp(struct buf *bp) +{ + struct contig_mem *cp; + struct scsi_tape *un; + struct buf *cont_bp; + int remapped = 0; + + un = ddi_get_soft_state(st_state, MTUNIT(bp->b_edev)); + if (un == NULL) { + return (bp); + } + + /* try to get one contig_mem */ + cp = st_get_contig_mem(un, bp->b_bcount, KM_SLEEP); + if (!cp) { + scsi_log(ST_DEVINFO, st_label, CE_WARN, + "Cannot alloc contig buf for I/O for %lu blk size", + bp->b_bcount); + return (bp); + } + cont_bp = cp->cm_bp; + cp->cm_bp = bp; + + /* make sure that we "are" using un_sbufp for special I/O */ + if (bp == un->un_sbufp) { + ASSERT(un->un_sbuf_busy); + un->un_sbufp = cont_bp; + cp->cm_use_sbuf = 1; + } + + /* clone bp */ + cont_bp->b_bcount = bp->b_bcount; + cont_bp->b_resid = bp->b_resid; + cont_bp->b_iodone = st_bigblk_xfer_done; + cont_bp->b_file = bp->b_file; + cont_bp->b_offset = bp->b_offset; + cont_bp->b_dip = bp->b_dip; + cont_bp->b_error = 0; + cont_bp->b_proc = NULL; + cont_bp->b_flags = bp->b_flags & ~(B_PAGEIO | B_PHYS | B_SHADOW); + cont_bp->b_shadow = NULL; + cont_bp->b_pages = NULL; + cont_bp->b_edev = bp->b_edev; + cont_bp->b_dev = bp->b_dev; + cont_bp->b_lblkno = bp->b_lblkno; + cont_bp->b_forw = bp->b_forw; + cont_bp->b_back = bp->b_back; + cont_bp->av_forw = bp->av_forw; + cont_bp->av_back = bp->av_back; + cont_bp->b_bufsize = bp->b_bufsize; + + /* get data in original bp */ + if (bp->b_flags & B_WRITE) { + if (bp->b_flags & (B_PHYS | B_PAGEIO)) { + bp_mapin(bp); + remapped = 1; + } + bcopy(bp->b_un.b_addr, cont_bp->b_un.b_addr, bp->b_bcount); + if (remapped) + bp_mapout(bp); + } + + return (cont_bp); +} +#endif diff --git a/usr/src/uts/common/sys/scsi/targets/stdef.h b/usr/src/uts/common/sys/scsi/targets/stdef.h index 685145d843..62967a5274 100644 --- a/usr/src/uts/common/sys/scsi/targets/stdef.h +++ b/usr/src/uts/common/sys/scsi/targets/stdef.h @@ -29,6 +29,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" +#include <sys/sunddi.h> #include <sys/note.h> #include <sys/condvar.h> #include <sys/kstat.h> @@ -508,6 +509,37 @@ struct read_blklim { uchar_t min_lo; /* Minimum block length, low byte */ }; +#ifdef _KERNEL + +#if defined(__i386) || defined(__amd64) +/* Data structure used in big block I/O on x86/x64 platform */ + +/* + * alloc more than one contig_mem, so mutiple I/O can be + * on-going simultaneously + */ +#define ST_MAX_CONTIG_MEM_NUM 3 + +/* + * 60K is used due to the limitation(size) of the intermediate buffer + * in DMA bind code(rootnex.c), which is 64K. If the I/O buf is page + * aligned, HBA can do 64K DMA, but if not, HBA can only do + * 64K - PAGESIZE = 60K DMA due to the copy to/from intermediate + * buffer will keep the page offset. + */ +#define ST_BIGBLK_XFER 60 * 1024 +struct contig_mem { + struct contig_mem *cm_next; + size_t cm_len; + caddr_t cm_addr; + ddi_acc_handle_t cm_acc_hdl; + struct buf *cm_bp; + int cm_use_sbuf; +}; +#endif + +#endif /* _KERNEL */ + /* * Private info for scsi tapes. Pointed to by the un_private pointer * of one of the SCSI_DEVICE chains. @@ -601,6 +633,14 @@ struct scsi_tape { uchar_t un_rqs_state; /* see define below */ caddr_t un_uscsi_rqs_buf; /* uscsi_rqs: buffer for RQS data */ uchar_t un_data_mod; /* Device required data mod */ + +#if defined(__i386) || defined(__amd64) + ddi_dma_handle_t un_contig_mem_hdl; + struct contig_mem *un_contig_mem; + int un_contig_mem_available_num; + int un_contig_mem_total_num; + kcondvar_t un_contig_mem_cv; +#endif }; /* * device error kstats |
