On Thu, 10 May 2007 12:21:53 +0200 Jens Axboe <[EMAIL PROTECTED]> wrote:
> This is what enables large commands. If we need to allocate an > sgtable that doesn't fit in a single page, allocate several > SCSI_MAX_SG_SEGMENTS sized tables and chain them together. > > We default to the safe setup of NOT chaining, for now. > > ... > > +/* > + * Should fit within a single page, and must be a power-of-2. > + */ > +#define SCSI_MAX_SG_SEGMENTS 128 But what units is it in? Bytes? sizeof(void*)? > +struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) > +{ > + struct scsi_host_sg_pool *sgp; > + struct scatterlist *sgl, *prev, *ret; > + unsigned int index; > + int this, left; > + > + BUG_ON(!cmd->use_sg); > + > + left = cmd->use_sg; > + ret = prev = NULL; > + do { > + this = left; > + if (this > SCSI_MAX_SG_SEGMENTS) { > + this = SCSI_MAX_SG_SEGMENTS; > + index = SG_MEMPOOL_NR - 1; > + } else > + index = scsi_sgtable_index(this); > + > + left -= this; > + > + /* > + * if we have more entries after this round, reserve a slot > + * for the chain pointer. > + */ > + if (left) > + left++; > + > + sgp = scsi_sg_pools + index; > + > + sgl = mempool_alloc(sgp->pool, gfp_mask); > + if (unlikely(!sgl)) > + goto enomem; > + > + memset(sgl, 0, sizeof(*sgl) * sgp->size); > + > + /* > + * first loop through, set initial index and return value > + */ > + if (!ret) { > + cmd->sglist_len = index; > + ret = sgl; > + } > + > + /* > + * chain previous sglist, if any. we know the previous > + * sglist must be the biggest one, or we would not have > + * ended up doing another loop. > + */ > + if (prev) > + sg_chain(prev, SCSI_MAX_SG_SEGMENTS, sgl); > + > + /* > + * don't allow subsequent mempool allocs to sleep, it would > + * violate the mempool principle. > + */ > + gfp_mask &= ~__GFP_WAIT; hrm. Might want to set __GFP_HIGH here too. > + prev = sgl; > + } while (left); > + > + /* > + * ->use_sg may get modified after dma mapping has potentially > + * shrunk the number of segments, so keep a copy of it for free. > + */ > + cmd->__use_sg = cmd->use_sg; > + return ret; > +enomem: > + if (ret) { > + /* > + * Free entries chained off ret. Since we were trying to > + * allocate another sglist, we know that all entries are of > + * the max size. > + */ > + sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1; > + prev = &ret[SCSI_MAX_SG_SEGMENTS - 1]; > + > + while ((sgl = sg_chain_ptr(ret)) != NULL) { > + ret = &sgl[SCSI_MAX_SG_SEGMENTS - 1]; > + mempool_free(sgl, sgp->pool); > + } > + > + mempool_free(prev, sgp->pool); > + } > + return NULL; > } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/