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) { if(buf->p != NULL) {
pbuf_free(buf->p); 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->payload = dataptr;
buf->p->len = buf->p->tot_len = size; buf->p->len = buf->p->tot_len = size;
buf->ptr = buf->p; 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; u16_t tmp;
/* Get a RAM based MTU sized pbuf */ /* 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->tot_len = rambuf->len = mtu;
rambuf->payload = buf; rambuf->payload = buf;
@ -349,7 +349,6 @@ ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
#ifdef IP_STATS #ifdef IP_STATS
++lwip_stats.ip_frag.xmit; ++lwip_stats.ip_frag.xmit;
#endif /* IP_STATS */ #endif /* IP_STATS */
pbuf_dechain(header);
pbuf_free(header); pbuf_free(header);
left -= cop; 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 * * PBUF_ROM: no buffer memory is allocated for the pbuf, even for
* protocol headers. Additional headers must be prepended * protocol headers. Additional headers must be prepended
* by allocating another pbuf and chain in to the front of * 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 * * PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
* the pbuf pool that is allocated during pbuf_init(). * the pbuf pool that is allocated during pbuf_init().
*/ */
@ -298,6 +305,7 @@ pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag)
((u32_t)p->payload % MEM_ALIGNMENT) == 0); ((u32_t)p->payload % MEM_ALIGNMENT) == 0);
break; break;
case PBUF_ROM: case PBUF_ROM:
case PBUF_REF:
/* If the pbuf should point to ROM, we only need to allocate /* If the pbuf should point to ROM, we only need to allocate
memory for the pbuf structure. */ memory for the pbuf structure. */
p = memp_mallocp(MEMP_PBUF); p = memp_mallocp(MEMP_PBUF);
@ -307,7 +315,10 @@ pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag)
p->payload = NULL; p->payload = NULL;
p->len = p->tot_len = size; p->len = p->tot_len = size;
p->next = NULL; p->next = NULL;
if (flag == PBUF_ROM)
p->flags = PBUF_FLAG_ROM; p->flags = PBUF_FLAG_ROM;
else
p->flags = PBUF_FLAG_REF;
break; break;
default: default:
LWIP_ASSERT("pbuf_alloc: erroneous flag", 0); LWIP_ASSERT("pbuf_alloc: erroneous flag", 0);
@ -409,7 +420,8 @@ pbuf_realloc(struct pbuf *p, u16_t size)
LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL || LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL ||
p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_RAM); p->flags == PBUF_FLAG_RAM ||
p->flags == PBUF_FLAG_REF);
if(p->tot_len <= size) { if(p->tot_len <= size) {
@ -439,6 +451,7 @@ pbuf_realloc(struct pbuf *p, u16_t size)
} }
break; break;
case PBUF_FLAG_ROM: case PBUF_FLAG_ROM:
case PBUF_FLAG_REF:
p->len = size; p->len = size;
break; break;
case PBUF_FLAG_RAM: case PBUF_FLAG_RAM:
@ -486,7 +499,8 @@ pbuf_header(struct pbuf *p, s16_t header_size)
{ {
void *payload; void *payload;
if(p->flags & PBUF_FLAG_ROM) { if(p->flags == PBUF_FLAG_ROM ||
p->flags == PBUF_FLAG_REF) {
return 1; return 1;
} }
@ -530,7 +544,8 @@ pbuf_free(struct pbuf *p)
LWIP_ASSERT("pbuf_free: sane flags", p->flags == PBUF_FLAG_POOL || LWIP_ASSERT("pbuf_free: sane flags", p->flags == PBUF_FLAG_POOL ||
p->flags == PBUF_FLAG_ROM || 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); LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
@ -554,7 +569,7 @@ pbuf_free(struct pbuf *p)
q = p->next; q = p->next;
PBUF_POOL_FREE(p); PBUF_POOL_FREE(p);
} else { } else {
if(p->flags == PBUF_FLAG_ROM) { if(p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) {
q = p->next; q = p->next;
memp_freep(MEMP_PBUF, p); memp_freep(MEMP_PBUF, p);
} else { } else {
@ -564,6 +579,15 @@ pbuf_free(struct pbuf *p)
} }
p = q; 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; ++count;
} }
pbuf_refresh(); pbuf_refresh();
@ -673,56 +697,71 @@ pbuf_dechain(struct pbuf *p)
return q; 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 * struct pbuf *
pbuf_unref(struct pbuf *f) pbuf_unref(struct pbuf *f)
{ {
struct pbuf *p, *q; struct pbuf *p, *prev, *q, *top;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: %p \n", (void*)f)); DEBUGF(PBUF_DEBUG, ("pbuf_unref: %p \n", (void*)f));
/* first pbuf is of type PBUF_REF? */
if (f->flags == PBUF_FLAG_REF) prev = 0;
p = f;
top = f;
do
{
/* pbuf is of type PBUF_REF? */
if (p->flags == PBUF_FLAG_REF)
{ {
/* allocate a pbuf (w/ payload) fully in RAM */ /* allocate a pbuf (w/ payload) fully in RAM */
p = pbuf_alloc(PBUF_RAW, f->len, PBUF_RAM); /* PBUF_POOL buffers are faster if we can use them */
if (p != 0) 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)
{ {
int i;
unsigned char *src, *dst;
/* copy pbuf struct */ /* copy pbuf struct */
p->next = f->next; q->next = p->next;
src = f->payload; if (prev)
dst = p->payload; /* Break chain and insert new pbuf instead */
i = 0; prev->next = q;
/* copy payload to RAM pbuf */ else
while(i < p->len) top = q;
{ p->next = NULL;
*dst = *src;
dst++; memcpy(q->payload, p->payload, p->len);
src++; q->tot_len = p->tot_len;
} q->len = p->len;
f->next = NULL;
/* Don't copy ref, since someone else might be using the old buffer */
/* de-allocate PBUF_REF */ /* de-allocate PBUF_REF */
pbuf_free(f); /* pbuf is not freed because it is assumed that some upper level
f = p; program has a direct pointer to this pbuf and will free it. */
DEBUGF(PBUF_DEBUG, ("pbuf_unref: succesful %p \n", (void *)f)); p = q;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: succesful %p \n", (void *)p));
} }
else else
{ {
/* deallocate chain */ /* deallocate chain */
pbuf_free(f); pbuf_free(top);
f = NULL;
DEBUGF(PBUF_DEBUG, ("pbuf_unref: failed\n")); DEBUGF(PBUF_DEBUG, ("pbuf_unref: failed\n"));
return NULL; return NULL;
} }
} }
/* p = previous pbuf == first pbuf */ prev = p;
p = f; p = p->next;
/* q = current pbuf */ } while (p);
q = f->next;
while (q != NULL) return top;
{
q = q->next;
}
return f;
} }

View File

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

View File

@ -184,7 +184,7 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
/* Do not copy the data. */ /* Do not copy the data. */
/* First, allocate a pbuf for holding 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")); DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n"));
goto memerr; goto memerr;
} }

View File

@ -411,7 +411,6 @@ udp_send(struct udp_pcb *pcb, struct pbuf *p)
} }
/* dechain and free the header pbuf */ /* dechain and free the header pbuf */
if(hdr != NULL) { if(hdr != NULL) {
pbuf_dechain(hdr);
pbuf_free(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; u8_t i, k;
#if ARP_QUEUEING #if ARP_QUEUEING
struct pbuf *p;
struct eth_hdr *ethhdr; struct eth_hdr *ethhdr;
#endif #endif
DEBUGF(ETHARP_DEBUG, ("update_arp_entry()")); 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; arp_table[i].ctime = 0;
#if ARP_QUEUEING #if ARP_QUEUEING
/* queued packet present? */ /* 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 */ /* fill-in Ethernet header */
ethhdr = arp_table[i].p->payload; ethhdr = p->payload;
for(k = 0; k < 6; ++k) { for(k = 0; k < 6; ++k) {
ethhdr->dest.addr[k] = ethaddr->addr[k]; ethhdr->dest.addr[k] = ethaddr->addr[k];
} }
ethhdr->type = htons(ETHTYPE_IP); ethhdr->type = htons(ETHTYPE_IP);
DEBUGF(ETHARP_DEBUG, ("update_arp_entry: sending queued IP packet.\n")); DEBUGF(ETHARP_DEBUG, ("update_arp_entry: sending queued IP packet.\n"));
/* send the queued IP packet */ /* send the queued IP packet */
netif->linkoutput(netif, arp_table[i].p); netif->linkoutput(netif, p);
/* free the queued IP packet */ /* free the queued IP packet */
pbuf_free(arp_table[i].p); pbuf_free(p);
/* remove queued packet from ARP entry (must be freed by the caller) */
arp_table[i].p = NULL;
} }
#endif #endif
return NULL; return NULL;