diff --git a/src/api/api_lib.c b/src/api/api_lib.c index e390628b..795d7c8a 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -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; diff --git a/src/core/ipv4/ip_frag.c b/src/core/ipv4/ip_frag.c index 426926b3..f40e3a1e 100644 --- a/src/core/ipv4/ip_frag.c +++ b/src/core/ipv4/ip_frag.c @@ -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; diff --git a/src/core/pbuf.c b/src/core/pbuf.c index fb638508..5f30719f 100644 --- a/src/core/pbuf.c +++ b/src/core/pbuf.c @@ -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; } diff --git a/src/core/tcp.c b/src/core/tcp.c index d43ddaec..65f74e3d 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -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; } /*-----------------------------------------------------------------------------------*/ diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 29b8f4fd..8a411fe5 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -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; } diff --git a/src/core/udp.c b/src/core/udp.c index 631305d3..eaa446f7 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -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); } diff --git a/src/netif/etharp.c b/src/netif/etharp.c index dc4f0cac..022a0e50 100644 --- a/src/netif/etharp.c +++ b/src/netif/etharp.c @@ -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;