tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: pool_cache interface and memory constraints
On On 2011-11-28 at 15:14 David Young wrote
> On Sat, Nov 26, 2011 at 11:04:56AM -0700, Sverre Froyen wrote:
> > Ideally, we would need separate pool caches for PCI, ISA, etc., each with
> > its own set of constraints. These constraints are part of the DMA tag
> > structures. It would therefore be nice to be able to access the pool
> > cache by the DMA tag and the size rather than a pool cache name string.
> > Should pool cache (and pool) be extended to support this?
>
> It may help to have a look at the way that I handle this in ixgbe(4).
> Start at ixgbe_jcl_reinit() and then have a look at ixgbe_getjcl().
>
> Many improvements to ixgbe(4)'s scheme can be made. For example,
> instead of a freelist protected by a mutex (ixgbe_extmem_head_t), a
> pool_cache of ixgbe_extmem_t's could be used. Also, the system should
> arbitrate between drivers for access to DMA buffers---that would be a
> big improvement over the current mbuf-cluster scheme where a few driver
> instances can starve all others of clusters.
Thanks for the comments! I decided to try to write some code (attached)
to illustrate my ideas. The code is incomplete and untested but I think
that it can provide an idea of what I'm thinking about. I would appreciate
comments about its feasibility. In particular, will pool_cache and bus_dma
work together the way I have it?
Individual drivers would request the pools they need in their attach methods
and pass the pool handles around as needed.
Regards,
Sverre
/*
Mbuf_pool_cache_init sets up a DMA safe pool_cache for the
specified bus and size. The pool_cace will use bus_dmamem_alloc
as its memory allocator. Mbuf_pool_cache_init may be called
multiple times for a given bus and size. Subsequent calls
returns the original pool_cache and increments a reference count.
Mbuf_pool_cache_init should be called from bus or device attach
methods as needed.
Mbuf_pool_cache_destroy should similarly be called from a bus or
device detach method. The reference counter is used to destroy
the pool_cache when appropriate.
*/
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/pool.h>
#include <sys/bus.h>
/* The mbuf_pool_item list */
static TAILQ_HEAD(, mbuf_pool_item) mbuf_pool_head =
TAILQ_HEAD_INITIALIZER(mbuf_pool_head);
struct mbuf_pool_item {
TAILQ_ENTRY(mbuf_pool_item) mbuf_pool_list;
bus_dma_tag_t mpi_bus_tag;
unsigned int mpi_size;
char *mpi_name;
pool_cache_t mpi_pc;
unsigned int mpi_refcnt;
};
static bool mbuf_pool_initialized = 0;
static kmutex_t mbuf_pool_lock;
static struct pool_allocator mbuf_pool_allocator;
#define MBUF_POOL_TO_MPI(pool) ((struct mbuf_pool_item *)(pool->pr_qcache))
struct mbuf_pool_item *
mbuf_pool_get_pool_item(pool_cache_t pc, bus_dma_tag_t tag, unsigned int
size);
char *
mbuf_pool_get_pool_name(bus_dma_tag_t tag, unsigned int size);
pool_cache_t
mbuf_pool_cache_init(bus_dma_tag_t tag, unsigned int size);
void
mbuf_pool_cache_destroy(pool_cache_t pc);
void *
mbuf_pool_cache_get_paddr(pool_cache_t pc, int flags, paddr_t *pap);
void
mbuf_pool_cache_put_paddr(pool_cache_t pc, void *object, paddr_t pa);
/*
* Custom pool alloc and free methods.
*/
static void *
mbuf_pool_poolpage_alloc(struct pool *pool, int prflags)
{
struct mbuf_pool_item *mpi;
int error, nsegs;
bus_dma_segment_t seg;
mpi = MBUF_POOL_TO_MPI(pool);
/* XXX verify alignment arg (mpi->mpi_size) */
error = bus_dmamem_alloc(mpi->mpi_bus_tag, pool->pr_alloc->pa_pagesz,
mpi->mpi_size, 0, &seg, 1, &nsegs, BUS_DMA_NOWAIT);
return (error == 0 && nsegs == 1) ? (void *)(&seg) : NULL;
}
static void
mbuf_pool_poolpage_free(struct pool *pool, void *addr)
{
struct mbuf_pool_item *mpi;
mpi = MBUF_POOL_TO_MPI(pool);
/* ... */
}
/*
* Return the mbuf_pool_item struct that matches pc or tag and size.
* Must be called with mutex held.
*/
struct mbuf_pool_item *
mbuf_pool_get_pool_item(pool_cache_t pc, bus_dma_tag_t tag, unsigned int size)
{
struct mbuf_pool_item *mpi = NULL, *mpi1;
TAILQ_FOREACH(mpi1, &mbuf_pool_head, mbuf_pool_list) {
if (mpi1->mpi_pc == pc ||
(mpi1->mpi_size == size && mpi1->mpi_bus_tag == tag)) {
mpi = mpi1;
break;
}
}
return mpi;
}
char *
mbuf_pool_get_pool_name(bus_dma_tag_t tag, unsigned int size)
{
/* ... */
return NULL;
}
pool_cache_t
mbuf_pool_cache_init(bus_dma_tag_t tag, unsigned int size)
{
pool_cache_t pc = NULL;
char *name;
struct mbuf_pool_item *mpi;
if (! mbuf_pool_initialized) {
/* XXX Racy code. Need a proper constructor? */
/* XXX IPL_NONE implies: cannot use in
an interrupt handler. Verify! */
mutex_init(&mbuf_pool_lock, MUTEX_DEFAULT, IPL_NONE);
mbuf_pool_initialized = true;
}
mutex_enter(&mbuf_pool_lock);
/* Protect by mutex in order to avoid race
with mbuf_pool_cache_destroy */
/* Existing mbuf_pool_cache? */
mpi = mbuf_pool_get_pool_item(NULL, tag, size);
if (mpi == NULL) {
/* Create a new pool cache */
mpi = kmem_alloc(sizeof(struct mbuf_pool_item), KM_SLEEP);
if (mpi == NULL)
goto fail;
mpi->mpi_bus_tag = tag;
mpi->mpi_size = size;
/* Pool caches must be named - make up a name. */
name = mbuf_pool_get_pool_name(tag, size);
mpi->mpi_name = name;
mbuf_pool_allocator.pa_alloc = &mbuf_pool_poolpage_alloc;
mbuf_pool_allocator.pa_free = &mbuf_pool_poolpage_free;
pc = pool_cache_init(size, 0, 0, 0, name,
&mbuf_pool_allocator, IPL_VM, NULL, NULL, NULL);
if (pc == NULL) {
kmem_free(mpi, sizeof(struct mbuf_pool_item));
goto fail;
}
/* mpi is needed in mbuf_pool_poolpage_alloc/free */
/* XXX is this OK? */
pc->pc_pool.pr_qcache = mpi;
mpi->mpi_pc = pc;
mpi->mpi_refcnt = 1;
/* Add the mbuf_pool_item to the mbuf pool item list. */
TAILQ_INSERT_TAIL(&mbuf_pool_head, mpi, mbuf_pool_list);
}
else {
/* Increment refcount and return the existing pool cache */
mpi->mpi_refcnt++;
pc = mpi->mpi_pc;
}
fail: mutex_exit(&mbuf_pool_lock);
return pc;
}
/* XXX should this method use a pool_cache_t pc argument instead? */
void
mbuf_pool_cache_destroy(pool_cache_t pc)
{
struct mbuf_pool_item *mpi;
mutex_enter(&mbuf_pool_lock);
mpi = mbuf_pool_get_pool_item(pc, NULL, 0);
KASSERT(mpi != NULL);
if (mpi->mpi_refcnt <= 1) {
/* Pool cache is no longer needed */
pool_cache_destroy(mpi->mpi_pc);
TAILQ_REMOVE(&mbuf_pool_head, mpi, mbuf_pool_list);
kmem_free(mpi, sizeof(struct mbuf_pool_item));
}
else {
mpi->mpi_refcnt--;
}
if (TAILQ_EMPTY(&mbuf_pool_head)) {
/* XXX Add code here that un-initializes
this object when appropriate. */
/* XXX OK to distroy a held mutex? */
/* XXX Racy code. */
mutex_destroy(&mbuf_pool_lock);
mbuf_pool_initialized = false;
}
if (mbuf_pool_initialized)
mutex_exit(&mbuf_pool_lock);
}
/* XXX These methods may not be needed. Why not call
the pool_cache methods instead? */
/* Perhaps implement OpenBSD's livelock solution? */
void *
mbuf_pool_cache_get_paddr(pool_cache_t pc, int flags, paddr_t *pap)
{
return pool_cache_get_paddr(pc, flags, pap);
}
void
mbuf_pool_cache_put_paddr(pool_cache_t pc, void *object, paddr_t pa)
{
return pool_cache_put_paddr(pc, object, pa);
}
/*
Implement these as needed:
mbuf_pool_cache_get
mbuf_pool_cache_put
mbuf_pool_cache_destruct_object
mbuf_pool_cache_invalidate
mbuf_pool_cache_sethiwat
mbuf_pool_cache_setlowat
mbuf_pool_cache_sethardlimit
*/
Home |
Main Index |
Thread Index |
Old Index