/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Multidata, as described in the following papers:
*
* Adi Masputra,
* Multidata V.2: VA-Disjoint Packet Extents Framework Interface
* Design Specification. August 2004.
* Available as http://sac.sfbay/PSARC/2004/594/materials/mmd2.pdf.
*
* Adi Masputra,
* Multidata Interface Design Specification. Sep 2002.
* Available as http://sac.sfbay/PSARC/2002/276/materials/mmd.pdf.
*
* Adi Masputra, Frank DiMambro, Kacheong Poon,
* An Efficient Networking Transmit Mechanism for Solaris:
* Multidata Transmit (MDT). May 2002.
* Available as http://sac.sfbay/PSARC/2002/276/materials/mdt.pdf.
*/
#include <sys/sysmacros.h>
#include <sys/multidata.h>
#include <sys/multidata_impl.h>
static int mmd_constructor(void *, void *, int);
static void mmd_destructor(void *, void *);
static int pdslab_constructor(void *, void *, int);
static void pdslab_destructor(void *, void *);
static int pattbl_constructor(void *, void *, int);
static void pattbl_destructor(void *, void *);
static void mmd_esballoc_free(caddr_t);
#pragma inline(pbuf_ref_valid)
#pragma inline(pdi_in_range)
#pragma inline(mmd_addpdesc_int)
static void mmd_destroy_pattbl(patbkt_t **);
#pragma inline(mmd_destroy_pattbl)
#pragma inline(mmd_find_pattr)
#pragma inline(mmd_destroy_pdesc)
#pragma inline(mmd_getpdesc)
/*
* Set to this to true to bypass pdesc bounds checking.
*/
/*
* Patchable kmem_cache flags.
*/
int mmd_kmem_flags = 0;
int pdslab_kmem_flags = 0;
int pattbl_kmem_flags = 0;
/*
* Alignment (in bytes) of our kmem caches.
*/
/*
* Default number of packet descriptors per descriptor slab. Making
* this too small will trigger more descriptor slab allocation; making
* it too large will create too many unclaimed descriptors.
*/
/*
* Default attribute hash table size. It's okay to set this to a small
* value (even to 1) because there aren't that many attributes currently
* defined, and because we assume there won't be many attributes associated
* with a Multidata at a given time. Increasing the size will reduce
* attribute search time (given a large number of attributes in a Multidata),
* and decreasing it will reduce the memory footprints and the overhead
* associated with managing the table.
*/
/*
* Attribute hash key.
*/
/*
* Structure that precedes each Multidata metadata.
*/
struct mmd_buf_info {
};
/*
* The size of each metadata buffer.
*/
#define MMD_CACHE_SIZE \
(sizeof (struct mmd_buf_info) + sizeof (multidata_t))
/*
* Called during startup in order to create the Multidata kmem caches.
*/
void
mmd_init(void)
{
}
/*
* Create a Multidata message block.
*/
{
/*
* Caller should never pass in a chain of mblks since we
* only care about the first one, hence the assertions.
*/
return (NULL);
return (NULL);
}
return (mmd);
}
/*
* Associate additional payload buffer to the Multidata.
*/
int
{
int i;
for (i = 0; i < MULTIDATA_MAX_PBUFS &&
/* duplicate entry */
"pld 0x%p to mmd 0x%p since it has been "
"previously added into slot %d (total %d)\n",
return (-1);
mmd->mmd_pbuf_cnt++;
return (i);
}
}
/* all slots are taken */
"since no slot space is left (total %d max %d)\n", (void *)pld_mp,
return (-1);
}
/*
* Multidata metadata kmem cache constructor routine.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* Multidata metadata kmem cache destructor routine.
*/
/* ARGSUSED */
static void
{
#ifdef DEBUG
int i;
#endif
#ifdef DEBUG
for (i = 0; i < MULTIDATA_MAX_PBUFS; i++)
#endif
}
/*
* Multidata message block free callback routine.
*/
static void
{
int i;
/* remove all packet descriptors and private attributes */
/* remove all global attributes */
/* remove all descriptor slabs */
mmd->mmd_slab_cnt--;
}
/* finally, free all associated message blocks */
}
for (i = 0; i < MULTIDATA_MAX_PBUFS; i++) {
mmd->mmd_pbuf_cnt--;
}
}
}
/*
* Multidata message block copy routine, called by copyb() when it
* encounters a M_MULTIDATA data block type. This routine should
* not be called by anyone other than copyb(), since it may go away
* (read: become static to this module) once some sort of copy callback
* routine is made available.
*/
mblk_t *
{
int idx, i;
#define FREE_PBUFS() { \
}
/* copy the header buffer */
return (NULL);
/* copy the payload buffer(s) */
for (i = 0; i < n_pbuf_cnt; i++) {
FREE_PBUFS();
return (NULL);
}
}
/* allocate new Multidata */
if (n_pbuf_cnt != 0)
FREE_PBUFS();
return (NULL);
}
/*
* Add payload buffer(s); upon success, leave n_pbuf array
* alone, as the newly-created Multidata had already contained
* the mblk pointers stored in the array. These will be freed
* along with the Multidata itself.
*/
if (idx < 0) {
FREE_PBUFS();
return (NULL);
}
}
/* copy over global attributes */
return (NULL);
}
/* copy over packet descriptors and their atttributes */
/* next pdesc */
1, B_TRUE);
/* skip if already removed */
continue;
}
/*
* Calculate new descriptor values based on the offset of
* each pointer relative to the associated buffer(s).
*/
}
/*
* We can't copy the pointers just like that,
* so calculate the relative offset.
*/
}
}
/* add the new descriptor to the new Multidata */
return (NULL);
}
}
return (n_bp);
}
/*
* Given a Multidata message block, return the Multidata metadata handle.
*/
{
return (NULL);
return (mmd);
}
/*
* Return the start and end addresses of the associated buffer(s).
*/
void
{
int i;
}
for (i = 0; i < mmd->mmd_pbuf_cnt; i++) {
}
}
/*
* Return the Multidata statistics.
*/
{
return (pd_cnt);
}
PDESC_HDRSIZE(pdi)))
/*
* Bounds check payload area(s).
*/
static boolean_t
{
int i = 0, idx;
return (B_FALSE);
}
PDESC_PLD_SPAN_SIZE(pdi, i)));
if (!valid) {
"pbuf_ref_valid: pdi 0x%p pld out of bound; "
"index %d has pld_cnt %d pbuf_idx %d "
"(mmd_pbuf_cnt %d), "
"pld_rptr 0x%p pld_wptr 0x%p len %d "
"(valid 0x%p-0x%p len %d)\n", (void *)pdi,
(int)PDESC_PLD_SPAN_SIZE(pdi, i),
}
/* advance to next entry */
i++;
pa++;
}
return (valid);
}
/*
* Add a packet descriptor to the Multidata.
*/
pdesc_t *
{
/* do the references refer to invalid memory regions? */
if (!mmd_speed_over_safety &&
return (NULL);
}
}
/*
* Internal routine to add a packet descriptor, called when mmd_addpdesc
* or mmd_copy tries to allocate and add a descriptor to a Multidata.
*/
static pdesc_t *
{
*err = 0;
/*
* Is slab list empty or the last-added slab is full? If so,
* allocate new slab for the descriptor; otherwise, use the
* last-added slab instead.
*/
return (NULL);
}
/* insert slab at end of list */
mmd->mmd_slab_cnt++;
} else {
}
/* copy over the descriptor info from caller */
mmd->mmd_hbuf_ref++;
mmd->mmd_pd_cnt++;
/* insert descriptor at end of list */
return (pd);
}
/*
* Packet descriptor slab kmem cache constructor routine.
*/
/* ARGSUSED */
static int
{
int i;
for (i = 0; i < cnt; i++) {
}
return (0);
}
/*
* Packet descriptor slab kmem cache destructor routine.
*/
/* ARGSUSED */
static void
{
}
/*
* Remove a packet descriptor from the in-use descriptor list,
* called by mmd_rempdesc or during free.
*/
static pdesc_t *
{
/* remove all local attributes */
/* don't decrease counts for a removed descriptor */
mmd->mmd_hbuf_ref--;
}
}
mmd->mmd_pd_cnt--;
}
return (pd_next);
}
/*
* Remove a packet descriptor from the Multidata.
*/
void
{
/*
* We can't deallocate the associated resources if the Multidata
* is shared with other threads, because it's possible that the
* descriptor handle value is held by those threads. That's why
* we simply mark the entry as "removed" and decrement the counts.
* If there are no other threads, then we free the descriptor.
*/
mmd->mmd_hbuf_ref--;
}
}
mmd->mmd_pd_cnt--;
} else {
}
}
/*
* A generic routine to traverse the packet descriptor in-use list.
*/
static pdesc_t *
{
if (!mutex_held)
/*
* We're called by mmd_get{first,last}pdesc, and so
* return either the first or last list element.
*/
} else {
/*
* We're called by mmd_get{next,prev}pdesc, and so
* return either the next or previous list element.
*/
}
/* skip element if it has been removed */
break;
}
if (!mutex_held)
/* return NULL if we're back at the beginning */
/* got an entry; copy descriptor info to caller */
return (pd);
}
/*
* Return the first packet descriptor in the in-use list.
*/
pdesc_t *
{
}
/*
* Return the last packet descriptor in the in-use list.
*/
pdesc_t *
{
}
/*
* Return the next packet descriptor in the in-use list.
*/
pdesc_t *
{
}
/*
* Return the previous packet descriptor in the in-use list.
*/
pdesc_t *
{
}
/*
* Check to see if pdi stretches over c_pdi; used to ensure that a packet
* descriptor's header and payload span may not be extended beyond the
* current boundaries.
*/
static boolean_t
{
int i;
return (B_FALSE);
/*
* We don't allow the number of span to be reduced, for the sake
* of simplicity. Instead, we provide PDESC_PLD_SPAN_CLEAR() to
* clear a packet descriptor. Note that we allow the span count to
* be increased, and the bounds check for the new one happens
* in pbuf_ref_valid.
*/
return (B_FALSE);
/* compare only those which are currently defined */
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Modify the layout of a packet descriptor.
*/
pdesc_t *
{
/* entry has been removed */
return (NULL);
/* caller doesn't intend to specify any buffer reference? */
return (NULL);
/* do the references refer to invalid memory regions? */
if (!mmd_speed_over_safety &&
return (NULL);
/* they're not subsets of current references? */
return (NULL);
/* copy over the descriptor info from caller */
return (pd);
}
/*
* Copy the contents of a packet descriptor into a new buffer. If the
* descriptor points to more than one buffer fragments, the contents
* of both fragments will be joined, with the header buffer fragment
* preceding the payload buffer fragment(s).
*/
mblk_t *
{
int i, len;
/* entry has been removed */
return (NULL);
}
/* allocate space large enough to hold the fragment(s) */
return (NULL);
}
/* copy over the header fragment */
}
/* copy over the payload fragment */
if (len > 0) {
}
}
}
return (mp);
}
/*
* Return a chain of mblks representing the Multidata packet.
*/
mblk_t *
{
/* entry has been removed */
return (NULL);
/* duplicate header buffer */
return (NULL);
}
/* duplicate payload buffer(s) */
int i;
/* skip empty ones */
if (PDESC_PLD_SPAN_SIZE(pdi, i) == 0)
continue;
return (NULL);
}
else
}
}
return (nmp);
}
/*
* Return duplicate message block(s) of the associated buffer(s).
*/
int
{
return (-1);
}
int i;
for (i = 0; i < mmd->mmd_pbuf_cnt; i++) {
return (-1);
}
else
}
}
return (0);
}
/*
* Return the layout of a packet descriptor.
*/
int
{
/* entry has been removed */
return (-1);
/* copy descriptor info to caller */
return (0);
}
/*
* Add a global or local attribute to a Multidata. Global attribute
* association is specified by a NULL packet descriptor.
*/
pattr_t *
{
/* pointer to the attribute hash table (local or global) */
/*
* See if the hash table has not yet been created; if so,
* we create the table and store its address atomically.
*/
return (NULL);
/* if someone got there first, use his table instead */
}
}
/* attribute of the same type already exists? */
return (NULL);
return (NULL);
if (persistent)
/* insert attribute at end of hash chain */
return (pa);
}
/*
* Attribute hash table kmem cache constructor routine.
*/
/* ARGSUSED */
static int
{
uint_t i;
/* first bucket contains the table size */
}
return (0);
}
/*
* Attribute hash table kmem cache destructor routine.
*/
/* ARGSUSED */
static void
{
uint_t i;
}
}
/*
* Destroy an attribute hash table, called by mmd_rempdesc or during free.
*/
static void
{
/* make sure caller passes in the first bucket */
/* destroy the contents of each bucket */
/* we ought to be exclusive at this point */
}
}
/* commit all previous stores */
}
/*
* Copy the contents of an attribute hash table, called by mmd_copy.
*/
static int
int kmflags)
{
/* make sure caller passes in the first bucket */
/* skip if it's removed */
continue;
}
return (-1);
}
/* copy over the contents */
}
}
return (0);
}
/*
* Search for an attribute type within an attribute hash bucket.
*/
static pattr_t *
{
/* return a match; we treat removed entry as non-existent */
break;
}
}
/*
* Remove an attribute from a Multidata.
*/
void
{
/* ignore if attribute was marked as persistent */
return;
/*
* We can't deallocate the associated resources if the Multidata
* is shared with other threads, because it's possible that the
* attribute handle value is held by those threads. That's why
* we simply mark the entry as "removed". If there are no other
* threads, then we free the attribute.
*/
} else {
}
}
/*
* Find an attribute (according to its type) and return its handle.
*/
pattr_t *
{
/* get the right attribute hash table (local or global) */
/* attribute hash table doesn't exist? */
return (NULL);
}
return (pa);
}
/*
* Return total size of buffers and total size of areas referenced
* by all in-use (unremoved) packet descriptors.
*/
void
{
int i;
*ptotal = 0;
for (i = 0; i < mmd->mmd_pbuf_cnt; i++) {
}
}
*pinuse = 0;
/* first pdesc */
/* next pdesc */
/* skip over removed descriptor */
continue;
}
}
}
}