Mercurial > illumos > illumos-gate
changeset 2539:2a06303a9684
6430355 sata framework needs to handle misaligned data buffers
6443108 sata framework: sata_dma_buf_setup() cannot call kmem_zalloc with KM_SLEEP flag
6457866 sata framework: assert that ddi_dma_sync succeeds with length and offset 0
author | pawelw |
---|---|
date | Fri, 11 Aug 2006 20:01:05 -0700 |
parents | b5894d78c55a |
children | 8af2e04292bf |
files | usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/sys/sata/impl/sata.h |
diffstat | 2 files changed, 202 insertions(+), 60 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/impl/sata.c Fri Aug 11 18:11:49 2006 -0700 +++ b/usr/src/uts/common/io/sata/impl/sata.c Fri Aug 11 20:01:05 2006 -0700 @@ -67,6 +67,8 @@ #ifdef SATA_DEBUG #define SATA_LOG_D(args) sata_log args +uint64_t mbuf_count = 0; +uint64_t mbuffail_count = 0; #else #define SATA_LOG_D(arg) #endif @@ -273,6 +275,17 @@ static int sata_default_pkt_time = 60; /* 60 seconds */ /* + * Intermediate buffer device access attributes - they are required, + * but not necessarily used. + */ +static ddi_device_acc_attr_t sata_acc_attr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + + +/* * Mutexes protecting structures in multithreaded operations. * Because events are relatively rare, a single global mutex protecting * data structures should be sufficient. To increase performance, add @@ -3189,6 +3202,15 @@ spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private; if (spx->txlt_buf_dma_handle != NULL) { + if (spx->txlt_tmp_buf != NULL) { + ASSERT(spx->txlt_tmp_buf_handle != 0); + /* + * Intermediate DMA buffer was allocated. + * Free allocated buffer and associated access handle. + */ + ddi_dma_mem_free(&spx->txlt_tmp_buf_handle); + spx->txlt_tmp_buf = NULL; + } /* * Free DMA resources - cookies and handles */ @@ -3236,6 +3258,9 @@ * Implementation of scsi tran_sync_pkt. * * The assumption below is that pkt is unique - there is no need to check ap + * + * Synchronize DMA buffer and, if the intermediate buffer is used, copy data + * into/from the real buffer. */ static void sata_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) @@ -3245,20 +3270,36 @@ #endif int rval; sata_pkt_txlate_t *spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private; - + struct buf *bp; + int direction; + + ASSERT(spx != NULL); if (spx->txlt_buf_dma_handle != NULL) { + direction = spx->txlt_sata_pkt-> + satapkt_cmd.satacmd_flags.sata_data_direction; if (spx->txlt_sata_pkt != NULL && - spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags. - sata_data_direction != SATA_DIR_NODATA_XFER) { - rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, - (spx->txlt_sata_pkt->satapkt_cmd. - satacmd_flags.sata_data_direction & - SATA_DIR_WRITE) ? - DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU); - if (rval == DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_scsi_sync_pkt: sync pkt failed")); - } + direction != SATA_DIR_NODATA_XFER) { + if (spx->txlt_tmp_buf != NULL) { + /* Intermediate DMA buffer used */ + bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; + + if (direction & SATA_DIR_WRITE) { + bcopy(bp->b_un.b_addr, + spx->txlt_tmp_buf, bp->b_bcount); + } + } + /* Sync the buffer for device or for CPU */ + rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, + (direction & SATA_DIR_WRITE) ? + DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU); + ASSERT(rval == DDI_SUCCESS); + if (spx->txlt_tmp_buf != NULL && + !(direction & SATA_DIR_WRITE)) { + /* Intermediate DMA buffer used for read */ + bcopy(spx->txlt_tmp_buf, + bp->b_un.b_addr, bp->b_bcount); + } + } } } @@ -5714,13 +5755,26 @@ struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; struct scsi_extended_sense *sense; uint64_t lba; - + struct buf *bp; + int rval; if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) { /* Normal completion */ scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS; scsipkt->pkt_reason = CMD_CMPLT; *scsipkt->pkt_scbp = STATUS_GOOD; + if (spx->txlt_tmp_buf != NULL) { + /* Temporary buffer was used */ + bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; + if (bp->b_flags & B_READ) { + rval = ddi_dma_sync( + spx->txlt_buf_dma_handle, 0, 0, + DDI_DMA_SYNC_FORCPU); + ASSERT(rval == DDI_SUCCESS); + bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr, + bp->b_bcount); + } + } } else { /* * Something went wrong - analyze return @@ -8630,11 +8684,11 @@ uint64_t max_txfer_len; uint64_t cur_txfer_len; + ASSERT(spx->txlt_sata_pkt != NULL); bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; ASSERT(bp != NULL); - if (spx->txlt_buf_dma_handle == NULL) { /* * No DMA resources allocated so far - this is a first call @@ -8661,9 +8715,104 @@ if (flags & PKT_DMA_PARTIAL) dma_flags |= DDI_DMA_PARTIAL; - rval = ddi_dma_buf_bind_handle(spx->txlt_buf_dma_handle, - bp, dma_flags, callback, arg, - &cookie, &spx->txlt_curwin_num_dma_cookies); + /* + * Check buffer alignment and size against dma attributes + * Consider dma_attr_align only. There may be requests + * with the size lower then device granularity, but they + * will not read/write from/to the device, so no adjustment + * is necessary. The dma_attr_minxfer theoretically should + * be considered, but no HBA driver is checking it. + */ + if (IS_P2ALIGNED(bp->b_un.b_addr, + cur_dma_attr->dma_attr_align)) { + rval = ddi_dma_buf_bind_handle( + spx->txlt_buf_dma_handle, + bp, dma_flags, callback, arg, + &cookie, + &spx->txlt_curwin_num_dma_cookies); + } else { /* Buffer is not aligned */ + + int (*ddicallback)(caddr_t); + size_t bufsz; + + /* Check id sleeping is allowed */ + ddicallback = (callback == NULL_FUNC) ? + DDI_DMA_DONTWAIT : DDI_DMA_SLEEP; + + SATADBG2(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst, + "mis-aligned buffer: addr=0x%p, cnt=%lu", + (void *)bp->b_un.b_addr, bp->b_bcount); + + if (bp->b_flags & (B_PAGEIO|B_PHYS)) + /* + * CPU will need to access data in the buffer + * (for copying) so map it. + */ + bp_mapin(bp); + + ASSERT(spx->txlt_tmp_buf == NULL); + + /* Buffer may be padded by ddi_dma_mem_alloc()! */ + rval = ddi_dma_mem_alloc( + spx->txlt_buf_dma_handle, + bp->b_bcount, + &sata_acc_attr, + DDI_DMA_STREAMING, + ddicallback, NULL, + &spx->txlt_tmp_buf, + &bufsz, + &spx->txlt_tmp_buf_handle); + + if (rval != DDI_SUCCESS) { + /* DMA mapping failed */ + (void) ddi_dma_free_handle( + &spx->txlt_buf_dma_handle); + spx->txlt_buf_dma_handle = NULL; +#ifdef SATA_DEBUG + mbuffail_count++; +#endif + SATADBG1(SATA_DBG_DMA_SETUP, + spx->txlt_sata_hba_inst, + "sata_dma_buf_setup: " + "buf dma mem alloc failed %x\n", rval); + return (rval); + } + ASSERT(IS_P2ALIGNED(spx->txlt_tmp_buf, + cur_dma_attr->dma_attr_align)); + +#ifdef SATA_DEBUG + mbuf_count++; + + if (bp->b_bcount != bufsz) + /* + * This will require special handling, because + * DMA cookies will be based on the temporary + * buffer size, not the original buffer + * b_bcount, so the residue may have to + * be counted differently. + */ + SATADBG2(SATA_DBG_DMA_SETUP, + spx->txlt_sata_hba_inst, + "sata_dma_buf_setup: bp size %x != " + "bufsz %x\n", bp->b_bcount, bufsz); +#endif + if (dma_flags & DDI_DMA_WRITE) { + /* + * Write operation - copy data into + * an aligned temporary buffer. Buffer will be + * synced for device by ddi_dma_addr_bind_handle + */ + bcopy(bp->b_un.b_addr, spx->txlt_tmp_buf, + bp->b_bcount); + } + + rval = ddi_dma_addr_bind_handle( + spx->txlt_buf_dma_handle, + NULL, + spx->txlt_tmp_buf, + bufsz, dma_flags, ddicallback, 0, + &cookie, &spx->txlt_curwin_num_dma_cookies); + } switch (rval) { case DDI_DMA_PARTIAL_MAP: @@ -8675,6 +8824,11 @@ */ if (ddi_dma_numwin(spx->txlt_buf_dma_handle, &spx->txlt_num_dma_win) != DDI_SUCCESS) { + if (spx->txlt_tmp_buf != NULL) { + ddi_dma_mem_free( + &spx->txlt_tmp_buf_handle); + spx->txlt_tmp_buf = NULL; + } (void) ddi_dma_unbind_handle( spx->txlt_buf_dma_handle); (void) ddi_dma_free_handle( @@ -8695,6 +8849,11 @@ default: /* DMA mapping failed */ + if (spx->txlt_tmp_buf != NULL) { + ddi_dma_mem_free( + &spx->txlt_tmp_buf_handle); + spx->txlt_tmp_buf = NULL; + } (void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle); spx->txlt_buf_dma_handle = NULL; SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, @@ -8769,7 +8928,8 @@ /* Allocate new dma cookie array */ spx->txlt_dma_cookie_list = kmem_zalloc( sizeof (ddi_dma_cookie_t) * - spx->txlt_curwin_num_dma_cookies, KM_SLEEP); + spx->txlt_curwin_num_dma_cookies, + callback == NULL_FUNC ? KM_NOSLEEP : KM_SLEEP); spx->txlt_dma_cookie_list_len = spx->txlt_curwin_num_dma_cookies; } @@ -8868,11 +9028,24 @@ } ASSERT(cur_txfer_len != 0); - spx->txlt_total_residue -= cur_txfer_len; + if (cur_txfer_len <= bp->b_bcount) + spx->txlt_total_residue -= cur_txfer_len; + else + /* + * Temporary DMA buffer has been padded by + * ddi_dma_mem_alloc()! + * This requires special handling, because DMA cookies are + * based on the temporary buffer size, not the b_bcount, + * and we have extra bytes to transfer - but the packet + * residue has to stay correct because we will copy only + * the requested number of bytes. + */ + spx->txlt_total_residue -= bp->b_bcount; return (DDI_SUCCESS); } + /* * Fetch Device Identify data. * Send DEVICE IDENTIFY command to a device and get the device identify data. @@ -8961,13 +9134,7 @@ /* Update sata_drive_info */ rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_fetch_device_identify_data: " - "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(bp->b_un.b_addr, &sdinfo->satadrv_id, sizeof (sata_id_t)); @@ -11184,13 +11351,7 @@ sdinfo->satadrv_addr.cport))); rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_fetch_smart_data: " - "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)smart_data, sizeof (struct smart_data)); } @@ -11296,13 +11457,7 @@ rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_ext_smart_selftest_log: " - "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)ext_selftest_log, sizeof (struct smart_ext_selftest_log)); @@ -11404,13 +11559,7 @@ sdinfo->satadrv_addr.cport))); rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_smart_selftest_log: " - "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)selftest_log, sizeof (struct smart_selftest_log)); rval = 0; @@ -11511,12 +11660,7 @@ rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_smart_read_log: " "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(scmd->satacmd_bp->b_un.b_addr, smart_log, log_size * 512); rval = 0; } @@ -11616,13 +11760,7 @@ sdinfo->satadrv_addr.cport))); rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); - if (rval != DDI_SUCCESS) { - SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, - "sata_read_log_ext_directory: " - "sync pkt failed")); - rval = -1; - goto fail; - } + ASSERT(rval == DDI_SUCCESS); bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)logdir, sizeof (struct read_log_ext_directory)); rval = 0;
--- a/usr/src/uts/common/sys/sata/impl/sata.h Fri Aug 11 18:11:49 2006 -0700 +++ b/usr/src/uts/common/sys/sata/impl/sata.h Fri Aug 11 20:01:05 2006 -0700 @@ -405,6 +405,10 @@ int txlt_dma_cookie_list_len; /* alloc list len */ ddi_dma_cookie_t *txlt_dma_cookie_list; /* dma cookie list */ int txlt_num_dma_cookies; /* dma cookies in list */ + + /* temporary buffer access handle */ + ddi_acc_handle_t txlt_tmp_buf_handle; + caddr_t txlt_tmp_buf; /* temp buffer address */ } sata_pkt_txlate_t; _NOTE(SCHEME_PROTECTS_DATA("unshared data", sata_pkt_txlate))