Mercurial > illumos > illumos-gate
changeset 932:9e056184bc38
6333962 st_get_contig_mem() will potentially dereferrence NULL pointer
author | cz147101 |
---|---|
date | Wed, 16 Nov 2005 01:05:16 -0800 |
parents | 92fdf4004904 |
children | 7664625cf909 |
files | usr/src/uts/common/io/scsi/targets/st.c usr/src/uts/common/sys/scsi/targets/stdef.h |
diffstat | 2 files changed, 90 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/scsi/targets/st.c Tue Nov 15 22:46:56 2005 -0800 +++ b/usr/src/uts/common/io/scsi/targets/st.c Wed Nov 16 01:05:16 2005 -0800 @@ -74,6 +74,34 @@ } \ } +/* + * get an available contig mem header, cp. + * when big_enough is true, we will return NULL, if no big enough + * contig mem is found. + * when big_enough is false, we will try to find cp containing big + * enough contig mem. if not found, we will ruturn the last cp available. + * + * used by st_get_contig_mem() + */ +#define ST_GET_CONTIG_MEM_HEAD(un, cp, len, big_enough) { \ + struct contig_mem *tmp_cp = NULL; \ + for ((cp) = (un)->un_contig_mem; \ + (cp) != NULL; \ + tmp_cp = (cp), (cp) = (cp)->cm_next) { \ + if (((cp)->cm_len >= (len)) || \ + (!(big_enough) && ((cp)->cm_next == NULL))) { \ + if (tmp_cp == NULL) { \ + (un)->un_contig_mem = (cp)->cm_next; \ + } else { \ + tmp_cp->cm_next = (cp)->cm_next; \ + } \ + (cp)->cm_next = NULL; \ + (un)->un_contig_mem_available_num--; \ + break; \ + } \ + } \ +} + #define ST_NUM_MEMBERS(array) (sizeof (array) / sizeof (array[0])) /* @@ -2749,16 +2777,15 @@ 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)); + kmem_free(cp_temp, + sizeof (struct contig_mem) + biosize()); } un->un_contig_mem_total_num = 0; un->un_contig_mem_available_num = 0; un->un_contig_mem = NULL; + un->un_max_contig_mem_len = 0; #endif ST_DEBUG(ST_DEVINFO, st_label, SCSI_DEBUG, @@ -11497,29 +11524,31 @@ struct contig_mem *cp = NULL; ddi_acc_handle_t acc_hdl; caddr_t addr; + int big_enough = 0; 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--; + ST_GET_CONTIG_MEM_HEAD(un, cp, len, big_enough); } 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); + cp = (struct contig_mem *)kmem_zalloc( + sizeof (struct contig_mem) + biosize(), + alloc_flags); if (cp == NULL) { ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, "alloc contig_mem failure\n"); return (NULL); /* cannot get one */ } + cp->cm_bp = (struct buf *) + (((caddr_t)cp) + sizeof (struct contig_mem)); + bioinit(cp->cm_bp); mutex_enter(ST_MUTEX); un->un_contig_mem_total_num++; /* one more available */ } else { @@ -11532,10 +11561,7 @@ 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--; + ST_GET_CONTIG_MEM_HEAD(un, cp, len, big_enough); } else { mutex_exit(ST_MUTEX); ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG, @@ -11549,38 +11575,62 @@ 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, + DDI_DMA_STREAMING, 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); - } - } - } - + cp = NULL; + } else { + if (cp->cm_addr) { + /* release previous one before attach new one */ + ddi_dma_mem_free(&cp->cm_acc_hdl); + } + mutex_enter(ST_MUTEX); + un->un_max_contig_mem_len = + un->un_max_contig_mem_len >= len ? + un->un_max_contig_mem_len : len; + mutex_exit(ST_MUTEX); + + /* attach new mem to this cp */ + cp->cm_addr = addr; + cp->cm_acc_hdl = acc_hdl; + cp->cm_len = len; + + goto alloc_ok; /* get one usable cp */ + } + } else { + goto alloc_ok; /* get one usable cp */ + } + + /* cannot find/alloc a usable cp, when we get here */ + + if ((un->un_max_contig_mem_len < len) || + (alloc_flags != KM_SLEEP)) { + return (NULL); + } + + /* + * we're allowed to sleep, and there is one big enough + * contig mem in the system, which is currently in use, + * wait for it... + */ + mutex_enter(ST_MUTEX); + big_enough = 1; + do { + cv_wait(&un->un_contig_mem_cv, ST_MUTEX); + ST_GET_CONTIG_MEM_HEAD(un, cp, len, big_enough); + } while (cp == NULL); + mutex_exit(ST_MUTEX); + + /* we get the big enough contig mem, finally */ + +alloc_ok: /* init bp attached to this cp */ - bioinit(cp->cm_bp); + bioreset(cp->cm_bp); cp->cm_bp->b_un.b_addr = cp->cm_addr; cp->cm_bp->b_private = (void *)cp; + return (cp); }
--- a/usr/src/uts/common/sys/scsi/targets/stdef.h Tue Nov 15 22:46:56 2005 -0800 +++ b/usr/src/uts/common/sys/scsi/targets/stdef.h Wed Nov 16 01:05:16 2005 -0800 @@ -639,6 +639,7 @@ struct contig_mem *un_contig_mem; int un_contig_mem_available_num; int un_contig_mem_total_num; + size_t un_max_contig_mem_len; kcondvar_t un_contig_mem_cv; #endif };