34 |
* |
* |
35 |
*/ |
*/ |
36 |
|
|
37 |
#define NEW_PBUF_REALLOC 1 /* should fix bug #1903 */ |
#define NEW_PBUF_REALLOC 1 /* enabling this should fix bug #1903 */ |
38 |
|
#define PBUF_CHAIN_DOES_REFER 1 /** enabling this fixes bug #2968 */ |
39 |
|
|
40 |
#include "lwip/opt.h" |
#include "lwip/opt.h" |
41 |
|
|
206 |
* * PBUF_REF: no buffer memory is allocated for the pbuf, even for |
* * PBUF_REF: no buffer memory is allocated for the pbuf, even for |
207 |
* protocol headers. It is assumed that the pbuf is only |
* protocol headers. It is assumed that the pbuf is only |
208 |
* being used in a single thread. If the pbuf gets queued, |
* being used in a single thread. If the pbuf gets queued, |
209 |
* then pbuf_unref should be called to copy the buffer. |
* then pbuf_take should be called to copy the buffer. |
210 |
* * 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 |
211 |
* the pbuf pool that is allocated during pbuf_init(). |
* the pbuf pool that is allocated during pbuf_init(). |
212 |
*/ |
*/ |
415 |
* through the chain until we find the new endpoint in the pbuf chain. |
* through the chain until we find the new endpoint in the pbuf chain. |
416 |
* Then the pbuf that is right on the endpoint is resized and any |
* Then the pbuf that is right on the endpoint is resized and any |
417 |
* further pbufs on the chain are deallocated. |
* further pbufs on the chain are deallocated. |
418 |
* @bug #1903 should be fixed |
* @bug Cannot grow the size of a pbuf (chain). |
|
* @bug Does not grow pbuf chains |
|
419 |
*/ |
*/ |
420 |
/*-----------------------------------------------------------------------------------*/ |
/*-----------------------------------------------------------------------------------*/ |
421 |
#if NEW_PBUF_REALLOC |
#if NEW_PBUF_REALLOC |
599 |
|
|
600 |
return 0; |
return 0; |
601 |
} |
} |
602 |
/*-----------------------------------------------------------------------------------*/ |
|
603 |
/* pbuf_free(): |
/** |
604 |
|
* Free a pbuf (chain) from its user, de-allocate if zero users. |
605 |
|
* |
606 |
|
* For a single pbuf, decrement its reference count. If it reaches |
607 |
|
* zero, de-allocate the associated memory. |
608 |
|
* |
609 |
|
* For chained pbufs, all reference counts of the pbufs in the chain |
610 |
|
* are decremented. Only if the first pbuf reference count reaches |
611 |
|
* zero, all pbufs are de-allocated. |
612 |
* |
* |
613 |
* Decrements the reference count and deallocates the pbuf if the |
* @param pbuf pbuf (chain) to be freed from its user. |
614 |
* reference count is zero. If the pbuf is a chain all pbufs in the |
* |
615 |
* chain are deallocated. |
* @note The reference count should not decrease when inspecting the |
616 |
|
* pbuf chain from head to tail. |
617 |
|
* |
618 |
|
* @note Chained pbufs with different reference counts should really |
619 |
|
* not occur. Something that references to the first pbuf, has access |
620 |
|
* to the complete chain, so all references |
621 |
*/ |
*/ |
622 |
/*-----------------------------------------------------------------------------------*/ |
/*-----------------------------------------------------------------------------------*/ |
623 |
u8_t |
u8_t |
625 |
{ |
{ |
626 |
struct pbuf *q; |
struct pbuf *q; |
627 |
u8_t count = 0; |
u8_t count = 0; |
628 |
|
u16_t last_ref_count; |
629 |
SYS_ARCH_DECL_PROTECT(old_level); |
SYS_ARCH_DECL_PROTECT(old_level); |
630 |
|
|
631 |
if (p == NULL) { |
if (p == NULL) { |
640 |
p->flags == PBUF_FLAG_RAM || |
p->flags == PBUF_FLAG_RAM || |
641 |
p->flags == PBUF_FLAG_REF ); |
p->flags == PBUF_FLAG_REF ); |
642 |
|
|
643 |
|
LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); |
644 |
|
p->ref--; |
645 |
|
|
646 |
/* Since decrementing ref cannot be guarranteed to be a single machine operation |
/* Since decrementing ref cannot be guarranteed to be a single machine operation |
647 |
* we must protect it. Also, the later test of ref must be protected. |
* we must protect it. Also, the later test of ref must be protected. |
648 |
*/ |
*/ |
649 |
SYS_ARCH_PROTECT(old_level); |
SYS_ARCH_PROTECT(old_level); |
650 |
/* decrement individual reference count for each pbufs in chain */ |
/* decrement individual reference count for each pbuf in chain */ |
651 |
for (q = p; q != NULL; q = q->next) { |
for (q = p->next; q != NULL; q = q->next) { |
652 |
LWIP_ASSERT("pbuf_free: q->ref > 0", q->ref > 0); |
/* reference counts can be 0, as 2nd and further pbufs will |
653 |
q->ref--; |
only be freed if the head of the chain is freed */ |
654 |
|
LWIP_ASSERT("pbuf_free: q->ref >= 0", q->ref >= 0); |
655 |
|
/* decrease reference count, but do not wrap! */ |
656 |
|
if (q->ref > 0) |
657 |
|
q->ref--; |
658 |
} |
} |
659 |
|
|
660 |
/* if reference count == 0, actually deallocate pbuf */ |
/* first pbuf now no longer needed? */ |
661 |
if (p->ref == 0) { |
if (p->ref == 0) { |
662 |
SYS_ARCH_UNPROTECT(old_level); |
SYS_ARCH_UNPROTECT(old_level); |
663 |
|
|
754 |
|
|
755 |
SYS_ARCH_UNPROTECT(old_level); |
SYS_ARCH_UNPROTECT(old_level); |
756 |
} |
} |
757 |
/*-----------------------------------------------------------------------------------*/ |
|
758 |
/* pbuf_chain(): |
|
759 |
|
/** |
760 |
* |
* |
761 |
* Chains the two pbufs h and t together. The ->tot_len field of the |
* Link two pbuf (chains) together. |
762 |
* first pbuf (h) is adjusted. |
* |
763 |
|
* The ->tot_len field of the first pbuf (h) is adjusted. |
764 |
*/ |
*/ |
765 |
/*-----------------------------------------------------------------------------------*/ |
/*-----------------------------------------------------------------------------------*/ |
766 |
void |
void |
768 |
{ |
{ |
769 |
struct pbuf *p; |
struct pbuf *p; |
770 |
|
|
771 |
if(t == NULL) { |
LWIP_ASSERT("h != NULL", h != NULL); |
772 |
return; |
LWIP_ASSERT("t != NULL", t != NULL); |
773 |
|
|
774 |
|
/* proceed to last pbuf of chain */ |
775 |
|
for(p = h; p->next != NULL; p = p->next) { |
776 |
|
/* add length of second chain to totals of first chain */ |
777 |
|
p->tot_len += t->tot_len; |
778 |
} |
} |
779 |
for(p = h; p->next != NULL; p = p->next); |
/* chain */ |
780 |
p->next = t; |
p->next = t; |
781 |
h->tot_len += t->tot_len; |
#if PBUF_CHAIN_DOES_REFER /** TODO (WORK IN PROGRESS) */ |
782 |
|
/* t is now referenced to one more time */ |
783 |
|
pbuf_ref(t); |
784 |
|
DEBUGF(DEBUG_PBUF | DBG_FRESH | 2, ("pbuf_chain: referencing %p\n", q)); |
785 |
|
#endif |
786 |
} |
} |
787 |
|
|
788 |
/** |
/** |
789 |
* Dechains a pbuf from any succeeding pbufs in the chain. |
* Dechains the first pbuf from its succeeding pbufs in the chain. |
790 |
* |
* |
791 |
* Makes p->tot_len field equal to p->len. |
* Makes p->tot_len field equal to p->len. |
792 |
* @param p pbuf to dechain |
* @param p pbuf to dechain |
800 |
q = p->next; |
q = p->next; |
801 |
/* pbuf has successor in chain? */ |
/* pbuf has successor in chain? */ |
802 |
if (q != NULL) { |
if (q != NULL) { |
803 |
/* LW: shouldn't q->tot_len be already exactly this? (make this an assert?) */ |
/* tot_len invariant: (p->tot_len == p->len + p->next->tot_len) */ |
804 |
|
LWIP_ASSERT("p->tot_len = p->len + q->tot_len", p->tot_len = p->len + q->tot_len); |
805 |
|
/* enforce invariant if assertion is disabled */ |
806 |
q->tot_len = p->tot_len - p->len; |
q->tot_len = p->tot_len - p->len; |
807 |
} |
} |
808 |
/* decouple pbuf from remainder */ |
/* decouple pbuf from remainder */ |
809 |
p->tot_len = p->len; |
p->tot_len = p->len; |
810 |
p->next = NULL; |
p->next = NULL; |
811 |
|
#if PBUF_CHAIN_DOES_REFER /** TODO (WORK IN PROGRESS) */ |
812 |
|
/* q is no longer referenced by p */ |
813 |
|
pbuf_free(q); |
814 |
|
DEBUGF(DEBUG_PBUF | DBG_FRESH | 2, ("pbuf_dechain: unreferencing %p\n", q)); |
815 |
|
#endif |
816 |
return q; |
return q; |
817 |
} |
} |
818 |
|
|
819 |
/** |
/** |
820 |
* |
* |
821 |
* Replace any PBUF_REF pbufs of a chain into PBUF_POOL/RAM buffers. |
* Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs. |
822 |
* |
* |
823 |
* Go through pbuf chain and replace any PBUF_REF buffers with PBUF_POOL |
* Go through a pbuf chain and replace any PBUF_REF buffers |
824 |
* (or PBUF_RAM) buffers. |
* with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of |
825 |
|
* the referenced data. |
826 |
* |
* |
827 |
* Used to queue packets on behalf of the lwIP stack, such as ARP based |
* Used to queue packets on behalf of the lwIP stack, such as ARP based |
828 |
* queueing. |
* queueing. |
832 |
* @return Pointer to new head of pbuf chain. |
* @return Pointer to new head of pbuf chain. |
833 |
*/ |
*/ |
834 |
struct pbuf * |
struct pbuf * |
835 |
pbuf_unref(struct pbuf *f) |
pbuf_take(struct pbuf *f) |
836 |
{ |
{ |
837 |
struct pbuf *p, *prev, *top; |
struct pbuf *p, *prev, *top; |
838 |
LWIP_ASSERT("pbuf_unref: f != NULL", f != NULL); |
LWIP_ASSERT("pbuf_take: f != NULL", f != NULL); |
839 |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_unref(%p)\n", (void*)f)); |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)f)); |
840 |
|
|
841 |
prev = NULL; |
prev = NULL; |
842 |
p = f; |
p = f; |
849 |
{ |
{ |
850 |
/* the replacement pbuf */ |
/* the replacement pbuf */ |
851 |
struct pbuf *q; |
struct pbuf *q; |
852 |
q = NULL; |
q = NULL; |
853 |
DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_unref: encountered PBUF_REF %p\n", (void *)p)); |
DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p)); |
854 |
/* allocate a pbuf (w/ payload) fully in RAM */ |
/* allocate a pbuf (w/ payload) fully in RAM */ |
855 |
/* PBUF_POOL buffers are faster if we can use them */ |
/* PBUF_POOL buffers are faster if we can use them */ |
856 |
if (p->len <= PBUF_POOL_BUFSIZE) { |
if (p->len <= PBUF_POOL_BUFSIZE) { |
857 |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); |
858 |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_unref: Could not allocate PBUF_RAW\n")); |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAW\n")); |
859 |
} |
} |
860 |
/* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ |
/* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ |
861 |
if (q == NULL) { |
if (q == NULL) { |
862 |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); |
863 |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_unref: Could not allocate PBUF_POOL\n")); |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n")); |
864 |
} |
} |
865 |
if (q != NULL) |
if (q != NULL) |
866 |
{ |
{ |
878 |
q->len = p->len; |
q->len = p->len; |
879 |
/* do not copy ref, since someone else might be using the old buffer */ |
/* do not copy ref, since someone else might be using the old buffer */ |
880 |
/* pbuf is not freed, as this is the responsibility of the application */ |
/* pbuf is not freed, as this is the responsibility of the application */ |
881 |
DEBUGF(PBUF_DEBUG, ("pbuf_unref: replaced PBUF_REF %p with %q\n", (void *)p, (void *)q)); |
DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %q\n", (void *)p, (void *)q)); |
882 |
p = q; |
p = q; |
883 |
} |
} |
884 |
else |
else |
885 |
{ |
{ |
886 |
/* deallocate chain */ |
/* deallocate chain */ |
887 |
pbuf_free(top); |
pbuf_free(top); |
888 |
DEBUGF(PBUF_DEBUG | 2, ("pbuf_unref: failed to allocate replacement pbuf for %p\n", (void *)p)); |
DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p)); |
889 |
return NULL; |
return NULL; |
890 |
} |
} |
891 |
} |
} |
892 |
else { |
else { |
893 |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_unref: not PBUF_REF")); |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: not PBUF_REF")); |
894 |
} |
} |
895 |
|
|
896 |
prev = p; |
prev = p; |
897 |
p = p->next; |
p = p->next; |
898 |
} while (p); |
} while (p); |
899 |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_unref: end of chain reached.")); |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.")); |
900 |
|
|
901 |
return top; |
return top; |
902 |
} |
} |