Fixed up and made work a PBUF_REF type. Most of the code uses this now

instead of PBUF_ROM. This addition allows support of copy-on-demand where the
lower layers can call pbuf_unref() which tests for any PBUF_REF buffers and
replaces them with PBUF_POOL buffers. This is now used
everywhere. pbuf_unref() is called in ARP queueing and in the coldfire
driver, which puts frames on a DMA queue and frees them later.

Along with this change pbuf_free() now goes through the entire chain of
buffers and tests all the ref counters, not just the first one. Generally now
pbuf_ref_chain() should be called and not pbuf_ref(). This change was made
because it is possible for the head of the pbuf chain to have a different
count than the payload pbuf which might have been passed by the application.
This commit is contained in:
davidhaas 2003-03-19 22:14:49 +00:00
parent 2f7e4bd587
commit 32d9f25a6f
7 changed files with 103 additions and 66 deletions

View File

@ -96,7 +96,7 @@ netbuf_ref(struct netbuf *buf, void *dataptr, u16_t size)
if(buf->p != NULL) {
pbuf_free(buf->p);
}
buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_ROM);
buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
buf->p->payload = dataptr;
buf->p->len = buf->p->tot_len = size;
buf->ptr = buf->p;

View File

@ -299,7 +299,7 @@ ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
u16_t tmp;
/* Get a RAM based MTU sized pbuf */
rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_ROM);
rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
rambuf->tot_len = rambuf->len = mtu;
rambuf->payload = buf;
@ -349,7 +349,6 @@ ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
#ifdef IP_STATS
++lwip_stats.ip_frag.xmit;
#endif /* IP_STATS */
pbuf_dechain(header);
pbuf_free(header);
left -= cop;

View File

@ -200,7 +200,14 @@ pbuf_pool_free(struct pbuf *p)
* * PBUF_ROM: no buffer memory is allocated for the pbuf, even for
* protocol headers. Additional headers must be prepended
* by allocating another pbuf and chain in to the front of
* the ROM pbuf.
* the ROM pbuf. It is assumed that the memory used is really
* similar to ROM in that it is immutable and will not be
* changed. Memory which is dynamic should generally not
* be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
* * PBUF_REF: no buffer memory is allocated for the pbuf, even for
* protocol headers. It is assumed that the pbuf is only
* being used in a single thread. If the pbuf gets queued,
* then pbuf_unref should be called to copy the buffer.
* * PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
* the pbuf pool that is allocated during pbuf_init().
*/
@ -297,7 +304,8 @@ pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag)
LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
((u32_t)p->payload % MEM_ALIGNMENT) == 0);
break;
case PBUF_ROM:
case PBUF_ROM:
case PBUF_REF:
/* If the pbuf should point to ROM, we only need to allocate
memory for the pbuf structure. */
p = memp_mallocp(MEMP_PBUF);
@ -307,7 +315,10 @@ pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag)
p->payload = NULL;
p->len = p->tot_len = size;
p->next = NULL;
p->flags = PBUF_FLAG_ROM;
if (flag == PBUF_ROM)
p->flags = PBUF_FLAG_ROM;
else
p->flags = PBUF_FLAG_REF;
break;
default:
LWIP_ASSERT("pbuf_alloc: erroneous flag", 0);
@ -408,8 +419,9 @@ pbuf_realloc(struct pbuf *p, u16_t size)
u16_t rsize;
LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL ||
p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_RAM);
p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_RAM ||
p->flags == PBUF_FLAG_REF);
if(p->tot_len <= size) {
@ -438,7 +450,8 @@ pbuf_realloc(struct pbuf *p, u16_t size)
q = r;
}
break;
case PBUF_FLAG_ROM:
case PBUF_FLAG_ROM:
case PBUF_FLAG_REF:
p->len = size;
break;
case PBUF_FLAG_RAM:
@ -486,7 +499,8 @@ pbuf_header(struct pbuf *p, s16_t header_size)
{
void *payload;
if(p->flags & PBUF_FLAG_ROM) {
if(p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_REF) {
return 1;
}
@ -530,7 +544,8 @@ pbuf_free(struct pbuf *p)
LWIP_ASSERT("pbuf_free: sane flags", p->flags == PBUF_FLAG_POOL ||
p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_RAM);
p->flags == PBUF_FLAG_RAM ||
p->flags == PBUF_FLAG_REF );
LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
@ -554,7 +569,7 @@ pbuf_free(struct pbuf *p)
q = p->next;
PBUF_POOL_FREE(p);
} else {
if(p->flags == PBUF_FLAG_ROM) {
if(p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) {
q = p->next;
memp_freep(MEMP_PBUF, p);
} else {
@ -564,6 +579,15 @@ pbuf_free(struct pbuf *p)
}
p = q;
/* Only free the next one in a chain if it's reference count is 0.
This allows buffer chains to have multiple headers pointing to them. */
if (p)
{
p->ref--;
if (p->ref > 0)
break;
}
++count;
}
pbuf_refresh();
@ -673,56 +697,71 @@ pbuf_dechain(struct pbuf *p)
return q;
}
/*-----------------------------------------------------------------------------------*/
/** Replace any pbufs of type PBUF_FLAG_REF with PBUF_POOL buffers.
Go through pbuf chain and replace any PBUF_REF buffers with PBUF_POOL
buffers. This is generally done for buffer chains which need to be queued
in some way (either on an output queue or on the arp queue). All pbufs
replaced will be freed immediately.
@param f Head of pbuf chain to process
@return Pointer to new head of pbuf chain.
*/
struct pbuf *
pbuf_unref(struct pbuf *f)
{
struct pbuf *p, *q;
struct pbuf *p, *prev, *q, *top;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: %p \n", (void*)f));
/* first pbuf is of type PBUF_REF? */
if (f->flags == PBUF_FLAG_REF)
{
/* allocate a pbuf (w/ payload) fully in RAM */
p = pbuf_alloc(PBUF_RAW, f->len, PBUF_RAM);
if (p != 0)
{
int i;
unsigned char *src, *dst;
/* copy pbuf struct */
p->next = f->next;
src = f->payload;
dst = p->payload;
i = 0;
/* copy payload to RAM pbuf */
while(i < p->len)
{
*dst = *src;
dst++;
src++;
}
f->next = NULL;
/* de-allocate PBUF_REF */
pbuf_free(f);
f = p;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: succesful %p \n", (void *)f));
}
else
{
/* deallocate chain */
pbuf_free(f);
f = NULL;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: failed\n"));
return NULL;
}
}
/* p = previous pbuf == first pbuf */
prev = 0;
p = f;
/* q = current pbuf */
q = f->next;
while (q != NULL)
top = f;
do
{
q = q->next;
}
return f;
/* pbuf is of type PBUF_REF? */
if (p->flags == PBUF_FLAG_REF)
{
/* allocate a pbuf (w/ payload) fully in RAM */
/* PBUF_POOL buffers are faster if we can use them */
if (p->len <= PBUF_POOL_BUFSIZE)
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL);
else
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM);
if (q != 0)
{
/* copy pbuf struct */
q->next = p->next;
if (prev)
/* Break chain and insert new pbuf instead */
prev->next = q;
else
top = q;
p->next = NULL;
memcpy(q->payload, p->payload, p->len);
q->tot_len = p->tot_len;
q->len = p->len;
/* Don't copy ref, since someone else might be using the old buffer */
/* de-allocate PBUF_REF */
/* pbuf is not freed because it is assumed that some upper level
program has a direct pointer to this pbuf and will free it. */
p = q;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: succesful %p \n", (void *)p));
}
else
{
/* deallocate chain */
pbuf_free(top);
DEBUGF(PBUF_DEBUG, ("pbuf_unref: failed\n"));
return NULL;
}
}
prev = p;
p = p->next;
} while (p);
return top;
}

View File

@ -713,7 +713,7 @@ tcp_seg_copy(struct tcp_seg *seg)
return NULL;
}
memcpy((char *)cseg, (const char *)seg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
pbuf_ref_chain(cseg->p);
return cseg;
}
/*-----------------------------------------------------------------------------------*/

View File

@ -184,7 +184,7 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
/* Do not copy the data. */
/* First, allocate a pbuf for holding the data. */
if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_REF)) == NULL) {
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n"));
goto memerr;
}

View File

@ -411,7 +411,6 @@ udp_send(struct udp_pcb *pcb, struct pbuf *p)
}
/* dechain and free the header pbuf */
if(hdr != NULL) {
pbuf_dechain(hdr);
pbuf_free(hdr);
}

View File

@ -235,6 +235,7 @@ update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *e
{
u8_t i, k;
#if ARP_QUEUEING
struct pbuf *p;
struct eth_hdr *ethhdr;
#endif
DEBUGF(ETHARP_DEBUG, ("update_arp_entry()"));
@ -270,21 +271,20 @@ update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *e
arp_table[i].ctime = 0;
#if ARP_QUEUEING
/* queued packet present? */
if(arp_table[i].p != NULL) {
if((p = arp_table[i].p) != NULL) {
/* Null out attached buffer immediately */
arp_table[i].p = NULL;
/* fill-in Ethernet header */
ethhdr = arp_table[i].p->payload;
ethhdr = p->payload;
for(k = 0; k < 6; ++k) {
ethhdr->dest.addr[k] = ethaddr->addr[k];
}
ethhdr->type = htons(ETHTYPE_IP);
DEBUGF(ETHARP_DEBUG, ("update_arp_entry: sending queued IP packet.\n"));
/* send the queued IP packet */
netif->linkoutput(netif, arp_table[i].p);
netif->linkoutput(netif, p);
/* free the queued IP packet */
pbuf_free(arp_table[i].p);
/* remove queued packet from ARP entry (must be freed by the caller) */
arp_table[i].p = NULL;
pbuf_free(p);
}
#endif
return NULL;