blob: 98d327a2db1cdca0661d45daca75aef6d3bfd14c [file] [log] [blame]
/**
* Copyright (c) Runtime Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Software in this file is based heavily on code written in the FreeBSD source
* code repostiory. While the code is written from scratch, it contains
* many of the ideas and logic flow in the original source, this is a
* derivative work, and the following license applies as well:
*
* Copyright (c) 1982, 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "os/os.h"
#include <string.h>
/**
* Initialize a pool of mbufs.
*
* @param omp The mbuf pool to initialize
* @param mp The memory pool that will hold this mbuf pool
* @param hdr_len The length of the header, in a header mbuf
* @param buf_len The length of the buffer itself.
* @param nbufs The number of buffers in the pool
*
* @return 0 on success, error code on failure.
*/
int
os_mbuf_pool_init(struct os_mbuf_pool *omp, struct os_mempool *mp,
uint16_t hdr_len, uint16_t buf_len, uint16_t nbufs)
{
omp->omp_hdr_len = hdr_len;
omp->omp_databuf_len = buf_len - sizeof(struct os_mbuf);
omp->omp_mbuf_count = nbufs;
omp->omp_pool = mp;
return (0);
}
/**
* Get an mbuf from the mbuf pool. The mbuf is allocated, and initialized
* prior to being returned.
*
* @param omp The mbuf pool to return the packet from
* @param leadingspace The amount of leadingspace to put before the data
* section by default.
*
* @return An initialized mbuf on success, and NULL on failure.
*/
struct os_mbuf *
os_mbuf_get(struct os_mbuf_pool *omp, uint16_t leadingspace)
{
struct os_mbuf *om;
om = os_memblock_get(omp->omp_pool);
if (!om) {
goto err;
}
SLIST_NEXT(om, om_next) = NULL;
om->om_flags = 0;
om->om_len = 0;
om->om_data = (&om->om_databuf[0] + leadingspace);
return (om);
err:
return (NULL);
}
/**
* Release a mbuf back to the pool
*
* @param omp The Mbuf pool to release back to
* @param om The Mbuf to release back to the pool
*
* @return 0 on success, -1 on failure
*/
int
os_mbuf_free(struct os_mbuf_pool *omp, struct os_mbuf *om)
{
int rc;
rc = os_memblock_put(omp->omp_pool, om);
if (rc != 0) {
goto err;
}
return (0);
err:
return (rc);
}
/**
* Free a chain of mbufs
*
* @param omp The mbuf pool to free the chain of mbufs into
* @param om The starting mbuf of the chain to free back into the pool
*
* @return 0 on success, -1 on failure
*/
int
os_mbuf_free_chain(struct os_mbuf_pool *omp, struct os_mbuf *om)
{
struct os_mbuf *next;
int rc;
while (om != NULL) {
next = SLIST_NEXT(om, om_next);
rc = os_mbuf_free(omp, om);
if (rc != 0) {
goto err;
}
om = next;
}
return (0);
err:
return (rc);
}
/**
* Copy a packet header from one mbuf to another.
*
* @param omp The mbuf pool associated with these buffers
* @param new_buf The new buffer to copy the packet header into
* @param old_buf The old buffer to copy the packet header from
*/
static inline void
_os_mbuf_copypkthdr(struct os_mbuf_pool *omp, struct os_mbuf *new_buf,
struct os_mbuf *old_buf)
{
memcpy(&new_buf->om_databuf[0], &old_buf->om_databuf[0],
sizeof(struct os_mbuf_pkthdr) + omp->omp_hdr_len);
}
/**
* Append data onto a mbuf
*
* @param omp The mbuf pool this mbuf was allocated out of
* @param om The mbuf to append the data onto
* @param data The data to append onto the mbuf
* @param len The length of the data to append
*
* @return 0 on success, and an error code on failure
*/
int
os_mbuf_append(struct os_mbuf_pool *omp, struct os_mbuf *om, void *data,
uint16_t len)
{
struct os_mbuf *last;
struct os_mbuf *new;
int remainder;
int space;
int rc;
if (omp == NULL || om == NULL) {
rc = OS_EINVAL;
goto err;
}
/* Scroll to last mbuf in the chain */
last = om;
while (SLIST_NEXT(last, om_next) != NULL) {
last = SLIST_NEXT(last, om_next);
}
remainder = len;
space = OS_MBUF_TRAILINGSPACE(omp, last);
/* If room in current mbuf, copy the first part of the data into the
* remaining space in that mbuf.
*/
if (space > 0) {
if (space > remainder) {
space = remainder;
}
memcpy(OS_MBUF_DATA(last, void *), data, space);
last->om_len += space;
data += space;
remainder -= space;
}
/* Take the remaining data, and keep allocating new mbufs and copying
* data into it, until data is exhausted.
*/
while (remainder > 0) {
new = os_mbuf_get(omp, OS_MBUF_START_OFF(omp));
if (!new) {
break;
}
new->om_len = min(omp->omp_databuf_len, remainder);
memcpy(OS_MBUF_DATA(om, void *), data, new->om_len);
data += new->om_len;
remainder -= new->om_len;
SLIST_NEXT(last, om_next) = new;
last = new;
}
/* Adjust the packet header length in the buffer */
if (OS_MBUF_IS_PKTHDR(om)) {
OS_MBUF_PKTHDR(om)->omp_len += len - remainder;
}
if (remainder != 0) {
rc = OS_ENOMEM;
goto err;
}
return (0);
err:
return (rc);
}
/**
* Duplicate a chain of mbufs. Return the start of the duplicated chain.
*
* @param omp The mbuf pool to duplicate out of
* @param om The mbuf chain to duplicate
*
* @return A pointer to the new chain of mbufs
*/
struct os_mbuf *
os_mbuf_dup(struct os_mbuf_pool *omp, struct os_mbuf *om)
{
struct os_mbuf *head;
struct os_mbuf *copy;
head = NULL;
copy = NULL;
for (; om != NULL; om = SLIST_NEXT(om, om_next)) {
if (head) {
SLIST_NEXT(copy, om_next) = os_mbuf_get(omp,
OS_MBUF_LEADINGSPACE(omp, om));
if (!SLIST_NEXT(copy, om_next)) {
os_mbuf_free_chain(omp, head);
goto err;
}
copy = SLIST_NEXT(copy, om_next);
} else {
head = os_mbuf_get(omp, OS_MBUF_LEADINGSPACE(omp, om));
if (!head) {
goto err;
}
if (om->om_flags & OS_MBUF_F_MASK(OS_MBUF_F_PKTHDR)) {
_os_mbuf_copypkthdr(omp, head, om);
}
copy = head;
}
copy->om_flags = om->om_flags;
copy->om_len = om->om_len;
memcpy(OS_MBUF_DATA(copy, uint8_t *), OS_MBUF_DATA(om, uint8_t *),
om->om_len);
}
return (head);
err:
return (NULL);
}
#if 0
/**
* Rearrange a mbuf chain so that len bytes are contiguous,
* and in the data area of an mbuf (so that OS_MBUF_DATA() will
* work on a structure of size len.) Returns the resulting
* mbuf chain on success, free's it and returns NULL on failure.
*
* If there is room, it will add up to "max_protohdr - len"
* extra bytes to the contiguous region, in an attempt to avoid being
* called next time.
*
* @param omp The mbuf pool to take the mbufs out of
* @param om The mbuf chain to make contiguous
* @param len The number of bytes in the chain to make contiguous
*
* @return The contiguous mbuf chain on success, NULL on failure.
*/
struct os_mbuf *
os_mbuf_pullup(struct os_mbuf_pool *omp, struct os_mbuf *om, uint16_t len)
{
struct os_mbuf *newm;
if (len > omp->omp_databuf_len) {
goto err;
}
/* Is 'n' bytes already contiguous? */
if (((uint8_t *) &om->om_databuf[0] + OS_MBUF_END_OFF(omp)) -
OS_MBUF_DATA(om, uint8_t *) >= len) {
newm = om;
goto done;
}
/* Nope, OK. Allocate a new buffer, and then go through and copy 'n'
* bytes into that buffer.
*/
newm = os_mbuf_get(omp, OS_MBUF_START_OFF(omp));
if (!newm) {
goto err;
}
written = 0;
while (written < len
done:
return (newm);
err:
if (om) {
os_mbuf_free_chain(om);
}
return (NULL);
}
#endif