2 |
* @file |
* @file |
3 |
* Packet buffers/chains management module |
* Packet buffers/chains management module |
4 |
*/ |
*/ |
5 |
|
|
6 |
/* |
/* |
7 |
* Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
* Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
8 |
* All rights reserved. |
* All rights reserved. |
35 |
* |
* |
36 |
*/ |
*/ |
37 |
|
|
|
#define NEW_PBUF_REALLOC 1 /* enabling this should fix bug #1903 */ |
|
|
#define PBUF_CHAIN_DOES_REFER 1 /** enabling this fixes bug #2968 */ |
|
|
|
|
38 |
#include "lwip/opt.h" |
#include "lwip/opt.h" |
39 |
|
|
40 |
#include "lwip/stats.h" |
#include "lwip/stats.h" |
59 |
static struct pbuf *pbuf_pool_alloc_cache = NULL; |
static struct pbuf *pbuf_pool_alloc_cache = NULL; |
60 |
static struct pbuf *pbuf_pool_free_cache = NULL; |
static struct pbuf *pbuf_pool_free_cache = NULL; |
61 |
|
|
62 |
/*-----------------------------------------------------------------------------------*/ |
/** |
63 |
/* pbuf_init(): |
* |
64 |
|
* Initializes the pbuf module. |
65 |
* |
* |
66 |
* Initializes the pbuf module. A large part of memory is allocated |
* A large part of memory is allocated for holding the pool of pbufs. |
67 |
* for holding the pool of pbufs. The size of the individual pbufs in |
* The size of the individual pbufs in the pool is given by the size |
68 |
* the pool is given by the size parameter, and the number of pbufs in |
* parameter, and the number of pbufs in the pool by the num parameter. |
|
* the pool by the num parameter. |
|
69 |
* |
* |
70 |
* After the memory has been allocated, the pbufs are set up. The |
* After the memory has been allocated, the pbufs are set up. The |
71 |
* ->next pointer in each pbuf is set up to point to the next pbuf in |
* ->next pointer in each pbuf is set up to point to the next pbuf in |
72 |
* the pool. |
* the pool. |
73 |
|
* |
74 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
75 |
void |
void |
76 |
pbuf_init(void) |
pbuf_init(void) |
77 |
{ |
{ |
78 |
struct pbuf *p, *q = 0; |
struct pbuf *p, *q = NULL; |
79 |
u16_t i; |
u16_t i; |
80 |
|
|
81 |
pbuf_pool = (struct pbuf *)&pbuf_pool_memory[0]; |
pbuf_pool = (struct pbuf *)&pbuf_pool_memory[0]; |
106 |
pbuf_pool_free_sem = sys_sem_new(1); |
pbuf_pool_free_sem = sys_sem_new(1); |
107 |
#endif |
#endif |
108 |
} |
} |
109 |
/*-----------------------------------------------------------------------------------*/ |
|
110 |
/* The following two functions are only called from pbuf_alloc(). */ |
/** |
111 |
/*-----------------------------------------------------------------------------------*/ |
* @internal only called from pbuf_alloc() |
112 |
|
*/ |
113 |
static struct pbuf * |
static struct pbuf * |
114 |
pbuf_pool_alloc(void) |
pbuf_pool_alloc(void) |
115 |
{ |
{ |
162 |
SYS_ARCH_UNPROTECT(old_level); |
SYS_ARCH_UNPROTECT(old_level); |
163 |
return p; |
return p; |
164 |
} |
} |
165 |
/*-----------------------------------------------------------------------------------*/ |
|
166 |
|
/** |
167 |
|
* @internal only called from pbuf_alloc() |
168 |
|
*/ |
169 |
static void |
static void |
170 |
pbuf_pool_free(struct pbuf *p) |
pbuf_pool_free(struct pbuf *p) |
171 |
{ |
{ |
187 |
} |
} |
188 |
SYS_ARCH_UNPROTECT(old_level); |
SYS_ARCH_UNPROTECT(old_level); |
189 |
} |
} |
190 |
/*-----------------------------------------------------------------------------------*/ |
|
191 |
/* pbuf_alloc(): |
/** |
192 |
* |
* |
193 |
* Allocates a pbuf at protocol layer l. The actual memory allocated |
* Allocates a pbuf at protocol layer l. |
194 |
* for the pbuf is determined by the layer at which the pbuf is |
* The actual memory allocated for the pbuf is determined by the |
195 |
* allocated and the requested size (from the size parameter). The |
* layer at which the pbuf is allocated and the requested size |
196 |
* flag parameter decides how and where the pbuf should be allocated |
* (from the size parameter). The flag parameter decides how and |
197 |
* as follows: |
* where the pbuf should be allocated as follows: |
198 |
* |
* |
199 |
* * PBUF_RAM: buffer memory for pbuf is allocated as one large |
* - PBUF_RAM: buffer memory for pbuf is allocated as one large |
200 |
* chunk. This includes protocol headers as well. |
* chunk. This includes protocol headers as well. |
201 |
* * PBUF_ROM: no buffer memory is allocated for the pbuf, even for |
* - PBUF_ROM: no buffer memory is allocated for the pbuf, even for |
202 |
* protocol headers. Additional headers must be prepended |
* protocol headers. Additional headers must be prepended |
203 |
* by allocating another pbuf and chain in to the front of |
* by allocating another pbuf and chain in to the front of |
204 |
* the ROM pbuf. It is assumed that the memory used is really |
* the ROM pbuf. It is assumed that the memory used is really |
205 |
* similar to ROM in that it is immutable and will not be |
* similar to ROM in that it is immutable and will not be |
206 |
* changed. Memory which is dynamic should generally not |
* changed. Memory which is dynamic should generally not |
207 |
* be attached to PBUF_ROM pbufs. Use PBUF_REF instead. |
* be attached to PBUF_ROM pbufs. Use PBUF_REF instead. |
208 |
* * PBUF_REF: no buffer memory is allocated for the pbuf, even for |
* - PBUF_REF: no buffer memory is allocated for the pbuf, even for |
209 |
* protocol headers. It is assumed that the pbuf is only |
* protocol headers. It is assumed that the pbuf is only |
210 |
* being used in a single thread. If the pbuf gets queued, |
* being used in a single thread. If the pbuf gets queued, |
211 |
* then pbuf_take should be called to copy the buffer. |
* then pbuf_take should be called to copy the buffer. |
212 |
* * 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 |
213 |
* the pbuf pool that is allocated during pbuf_init(). |
* the pbuf pool that is allocated during pbuf_init(). |
214 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
215 |
struct pbuf * |
struct pbuf * |
216 |
pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag) |
pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag) |
217 |
{ |
{ |
218 |
struct pbuf *p, *q, *r; |
struct pbuf *p, *q, *r; |
219 |
u16_t offset; |
u16_t offset; |
220 |
s32_t rsize; |
s32_t rem_len; |
221 |
|
|
222 |
/* determine header offset */ |
/* determine header offset */ |
223 |
offset = 0; |
offset = 0; |
224 |
switch(l) { |
switch (l) { |
225 |
case PBUF_TRANSPORT: |
case PBUF_TRANSPORT: |
226 |
offset += PBUF_TRANSPORT_HLEN; |
offset += PBUF_TRANSPORT_HLEN; |
227 |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
238 |
return NULL; |
return NULL; |
239 |
} |
} |
240 |
|
|
241 |
switch(flag) { |
switch (flag) { |
242 |
case PBUF_POOL: |
case PBUF_POOL: |
243 |
/* allocate head of pbuf chain into p */ |
/* allocate head of pbuf chain into p */ |
244 |
p = pbuf_pool_alloc(); |
p = pbuf_pool_alloc(); |
245 |
if(p == NULL) { |
if (p == NULL) { |
246 |
#ifdef PBUF_STATS |
#ifdef PBUF_STATS |
247 |
++lwip_stats.pbuf.err; |
++lwip_stats.pbuf.err; |
248 |
#endif /* PBUF_STATS */ |
#endif /* PBUF_STATS */ |
252 |
|
|
253 |
/* make the payload pointer points offset bytes into pbuf data memory */ |
/* make the payload pointer points offset bytes into pbuf data memory */ |
254 |
p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset))); |
p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset))); |
|
|
|
255 |
/* the total length of the pbuf is the requested size */ |
/* the total length of the pbuf is the requested size */ |
256 |
p->tot_len = size; |
p->tot_len = size; |
|
|
|
257 |
/* set the length of the first pbuf is the chain */ |
/* set the length of the first pbuf is the chain */ |
258 |
p->len = size > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: size; |
p->len = size > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: size; |
|
|
|
259 |
p->flags = PBUF_FLAG_POOL; |
p->flags = PBUF_FLAG_POOL; |
260 |
|
|
261 |
/* allocate the tail of the pbuf chain. */ |
/* allocate the tail of the pbuf chain. */ |
262 |
r = p; |
r = p; |
263 |
rsize = size - p->len; |
rem_len = size - p->len; |
264 |
while(rsize > 0) { |
while(rem_len > 0) { |
265 |
q = pbuf_pool_alloc(); |
q = pbuf_pool_alloc(); |
266 |
if (q == NULL) { |
if (q == NULL) { |
267 |
DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n")); |
DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n")); |
268 |
#ifdef PBUF_STATS |
#ifdef PBUF_STATS |
269 |
++lwip_stats.pbuf.err; |
++lwip_stats.pbuf.err; |
270 |
#endif /* PBUF_STATS */ |
#endif /* PBUF_STATS */ |
271 |
|
/* bail out unsuccesfully */ |
272 |
pbuf_pool_free(p); |
pbuf_pool_free(p); |
273 |
return NULL; |
return NULL; |
274 |
} |
} |
275 |
q->next = NULL; |
q->next = NULL; |
276 |
r->next = q; |
r->next = q; |
277 |
q->len = rsize > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rsize; |
q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len; |
278 |
q->flags = PBUF_FLAG_POOL; |
q->flags = PBUF_FLAG_POOL; |
279 |
q->payload = (void *)((u8_t *)q + sizeof(struct pbuf)); |
q->payload = (void *)((u8_t *)q + sizeof(struct pbuf)); |
280 |
r = q; |
r = q; |
281 |
q->ref = 1; |
q->ref = 1; |
282 |
rsize -= PBUF_POOL_BUFSIZE; |
rem_len -= PBUF_POOL_BUFSIZE; |
283 |
} |
} |
284 |
/* end of chain */ |
/* end of chain */ |
285 |
r->next = NULL; |
r->next = NULL; |
290 |
case PBUF_RAM: |
case PBUF_RAM: |
291 |
/* If pbuf is to be allocated in RAM, allocate memory for it. */ |
/* If pbuf is to be allocated in RAM, allocate memory for it. */ |
292 |
p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + size + offset)); |
p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + size + offset)); |
293 |
if(p == NULL) { |
if (p == NULL) { |
294 |
return NULL; |
return NULL; |
295 |
} |
} |
296 |
/* Set up internal structure of the pbuf. */ |
/* Set up internal structure of the pbuf. */ |
308 |
case PBUF_REF: |
case PBUF_REF: |
309 |
/* only allocate memory for the pbuf structure */ |
/* only allocate memory for the pbuf structure */ |
310 |
p = memp_mallocp(MEMP_PBUF); |
p = memp_mallocp(MEMP_PBUF); |
311 |
if(p == NULL) { |
if (p == NULL) { |
312 |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_REF.\n")); |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_REF.\n")); |
313 |
return NULL; |
return NULL; |
314 |
} |
} |
315 |
/* caller must set this field properly, afterwards */ |
/* caller must set this field properly, afterwards */ |
316 |
p->payload = NULL; |
p->payload = NULL; |
317 |
p->len = p->tot_len = size; |
p->len = p->tot_len = size; |
318 |
p->next = NULL; |
p->next = NULL; |
325 |
p->ref = 1; |
p->ref = 1; |
326 |
return p; |
return p; |
327 |
} |
} |
328 |
/*-----------------------------------------------------------------------------------*/ |
|
329 |
/* pbuf_refresh(): |
/** |
330 |
* |
* |
331 |
* Moves free buffers from the pbuf_pool_free_cache to the pbuf_pool |
* Moves free buffers from the pbuf_pool_free_cache to the pbuf_pool |
332 |
* list (if possible). |
* list (if possible). |
333 |
* |
* |
334 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
335 |
void |
void |
336 |
pbuf_refresh(void) |
pbuf_refresh(void) |
337 |
{ |
{ |
399 |
#endif /* SYS_LIGHTWEIGHT_PROT */ |
#endif /* SYS_LIGHTWEIGHT_PROT */ |
400 |
|
|
401 |
/** |
/** |
402 |
* Shrink a pbuf chain to a certain size. |
* Shrink a pbuf chain to a desired length. |
403 |
* |
* |
404 |
* @param p pbuf to shrink. |
* @param p pbuf to shrink. |
405 |
* @param size new size |
* @param new_len desired new length of pbuf chain |
406 |
* |
* |
407 |
* Depending on the desired size, the first few pbufs in a chain might |
* Depending on the desired length, the first few pbufs in a chain might |
408 |
* be skipped. |
* be skipped and left unchanged. The new last pbuf in the chain will be |
409 |
|
* resized, and any remaining pbufs will be freed. |
410 |
|
* |
411 |
* @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. |
* @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. |
412 |
* If the chain |
* |
413 |
* a pbuf chain, as it might be with both pbufs in dynamically |
* @bug Cannot grow the size of a pbuf (chain) (yet). |
|
* allocated RAM and for pbufs from the pbuf pool, we have to step |
|
|
* through the chain until we find the new endpoint in the pbuf chain. |
|
|
* Then the pbuf that is right on the endpoint is resized and any |
|
|
* further pbufs on the chain are deallocated. |
|
|
* @bug Cannot grow the size of a pbuf (chain). |
|
414 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
|
#if NEW_PBUF_REALLOC |
|
415 |
void |
void |
416 |
pbuf_realloc(struct pbuf *p, u16_t new_len) |
pbuf_realloc(struct pbuf *p, u16_t new_len) |
417 |
{ |
{ |
424 |
p->flags == PBUF_FLAG_RAM || |
p->flags == PBUF_FLAG_RAM || |
425 |
p->flags == PBUF_FLAG_REF); |
p->flags == PBUF_FLAG_REF); |
426 |
|
|
427 |
|
/* desired length larger than current length? */ |
428 |
if (new_len >= p->tot_len) { |
if (new_len >= p->tot_len) { |
429 |
/** enlarging not yet supported */ |
/* enlarging not yet supported */ |
430 |
return; |
return; |
431 |
} |
} |
432 |
|
|
433 |
/* { the pbuf chains grows by (new_len - p->tot_len) bytes } */ |
/* the pbuf chain grows by (new_len - p->tot_len) bytes |
434 |
|
* (which may be negative in case of shrinking) */ |
435 |
grow = new_len - p->tot_len; |
grow = new_len - p->tot_len; |
436 |
|
|
437 |
/* first, step over any pbufs that should remain in the chain */ |
/* first, step over any pbufs that should remain in the chain */ |
441 |
while (rem_len > q->len) { |
while (rem_len > q->len) { |
442 |
/* decrease remaining length by pbuf length */ |
/* decrease remaining length by pbuf length */ |
443 |
rem_len -= q->len; |
rem_len -= q->len; |
444 |
|
/* decrease total length indicator */ |
445 |
q->tot_len += grow; |
q->tot_len += grow; |
446 |
|
/* proceed to next pbuf in chain */ |
447 |
q = q->next; |
q = q->next; |
448 |
} |
} |
449 |
/* { we have now reached the new last pbuf } */ |
/* we have now reached the new last pbuf (in q) */ |
450 |
/* { rem_len == desired length for pbuf q } */ |
/* rem_len == desired length for pbuf q */ |
451 |
|
|
452 |
/* shrink allocated memory for PBUF_RAM */ |
/* shrink allocated memory for PBUF_RAM */ |
453 |
/* (other types merely adjust their length fields */ |
/* (other types merely adjust their length fields */ |
454 |
if ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len )) { |
if ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len)) { |
455 |
/* reallocate and adjust the length of the pbuf that will be split */ |
/* reallocate and adjust the length of the pbuf that will be split */ |
456 |
mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); |
mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); |
457 |
} |
} |
458 |
/* adjust length fields */ |
/* adjust length fields for new last pbuf */ |
459 |
q->len = rem_len; |
q->len = rem_len; |
460 |
q->tot_len = q->len; |
q->tot_len = q->len; |
461 |
|
|
462 |
/* deallocate any left over pbufs */ |
/* any remaining pbufs in chain? */ |
463 |
/* remember next pbuf in chain */ |
if (q->next != NULL) { |
464 |
r = q->next; |
/* free remaining pbufs in chain */ |
465 |
|
pbuf_free(q->next); |
466 |
|
} |
467 |
/* q is last packet in chain */ |
/* q is last packet in chain */ |
468 |
q->next = NULL; |
q->next = NULL; |
|
/* first pbuf to be dealloced */ |
|
|
q = r; |
|
|
/* any pbuf left? */ |
|
|
while(q != NULL) { |
|
|
/* remember next pbuf in chain */ |
|
|
r = q->next; |
|
|
/* deallocate pbuf */ |
|
|
if (q->flags == PBUF_FLAG_POOL) { |
|
|
PBUF_POOL_FREE(q); |
|
|
} else { |
|
|
pbuf_free(q); |
|
|
} |
|
|
q = r; |
|
|
} |
|
469 |
|
|
470 |
pbuf_refresh(); |
pbuf_refresh(); |
471 |
} |
} |
|
#else /* pbuf_realloc() of CVS version 1.23 */ |
|
|
void |
|
|
pbuf_realloc(struct pbuf *p, u16_t size) |
|
|
{ |
|
|
struct pbuf *q, *r; |
|
|
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_REF); |
|
|
|
|
|
if(p->tot_len <= size) { |
|
|
return; |
|
|
} |
|
|
|
|
|
switch(p->flags) { |
|
|
case PBUF_FLAG_POOL: |
|
|
/* First, step over any pbufs that should still be in the chain. */ |
|
|
rsize = size; |
|
|
q = p; |
|
|
while(rsize > q->len) { |
|
|
rsize -= q->len; |
|
|
q = q->next; |
|
|
} |
|
|
/* Adjust the length of the pbuf that will be halved. */ |
|
|
q->len = rsize; |
|
|
|
|
|
/* And deallocate any left over pbufs. */ |
|
|
r = q->next; |
|
|
q->next = NULL; |
|
|
q = r; |
|
|
while(q != NULL) { |
|
|
r = q->next; |
|
|
PBUF_POOL_FREE(q); |
|
|
q = r; |
|
|
} |
|
|
break; |
|
|
case PBUF_FLAG_ROM: |
|
|
case PBUF_FLAG_REF: |
|
|
p->len = size; |
|
|
break; |
|
|
case PBUF_FLAG_RAM: |
|
|
/* First, step over the pbufs that should still be in the chain. */ |
|
|
rsize = size; |
|
|
q = p; |
|
|
while(rsize > q->len) { |
|
|
rsize -= q->len; |
|
|
q = q->next; |
|
|
} |
|
|
if(q->flags == PBUF_FLAG_RAM) { |
|
|
/* Reallocate and adjust the length of the pbuf that will be halved. */ |
|
|
mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rsize); |
|
|
} |
|
|
|
|
|
q->len = rsize; |
|
|
|
|
|
/* And deallocate any left over pbufs. */ |
|
|
r = q->next; |
|
|
q->next = NULL; |
|
|
q = r; |
|
|
while(q != NULL) { |
|
|
r = q->next; |
|
|
pbuf_free(q); |
|
|
q = r; |
|
|
} |
|
|
break; |
|
|
} |
|
|
p->tot_len = size; |
|
|
|
|
|
pbuf_refresh(); |
|
|
} |
|
|
#endif |
|
472 |
|
|
473 |
/** |
/** |
474 |
* Decreases the header size by the given amount. |
* Tries to decrease the payload pointer by the given header size. |
475 |
* |
* |
476 |
* Adjusts the ->payload pointer so that space for a header appears in |
* Adjusts the ->payload pointer so that space for a header appears in |
477 |
* the pbuf. Also, the ->tot_len and ->len fields are adjusted. |
* the pbuf. Also, the ->tot_len and ->len fields are adjusted. |
481 |
* |
* |
482 |
* @return 1 on failure, 0 on succes. |
* @return 1 on failure, 0 on succes. |
483 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
484 |
u8_t |
u8_t |
485 |
pbuf_header(struct pbuf *p, s16_t header_size) |
pbuf_header(struct pbuf *p, s16_t header_size) |
486 |
{ |
{ |
487 |
void *payload; |
void *payload; |
488 |
/* referencing pbufs cannot be realloc()ed */ |
/* referencing pbufs cannot be realloc()ed */ |
489 |
|
/* TODO: WHY NOT? just adjust payload, tot_len and len? */ |
490 |
if (p->flags == PBUF_FLAG_ROM || |
if (p->flags == PBUF_FLAG_ROM || |
491 |
p->flags == PBUF_FLAG_REF) { |
p->flags == PBUF_FLAG_REF) { |
492 |
|
/* failure */ |
493 |
return 1; |
return 1; |
494 |
} |
} |
495 |
|
|
516 |
/** |
/** |
517 |
* Free a pbuf (chain) from its user, de-allocate if zero users. |
* Free a pbuf (chain) from its user, de-allocate if zero users. |
518 |
* |
* |
519 |
* For a single pbuf, decrement its reference count. If it reaches |
* Decrements the pbuf reference count. If it reaches |
520 |
* zero, de-allocate the associated memory. |
* zero, the pbuf is deallocated. |
521 |
* |
* |
522 |
* For chained pbufs, all reference counts of the pbufs in the chain |
* This is repeated for each pbuf in the chain, until a non-zero |
523 |
* are decremented. Only if the first pbuf reference count reaches |
* reference count is encountered, or the end of the chain is reached. |
|
* zero, all pbufs are de-allocated. |
|
524 |
* |
* |
525 |
* @param pbuf pbuf (chain) to be freed from its user. |
* @param pbuf pbuf (chain) to be freed from its user. |
526 |
* |
* |
527 |
* @note The reference count should not decrease when inspecting the |
* @return the number of unreferenced pbufs that were de-allocated |
528 |
* pbuf chain from head to tail. |
* from the head of the chain. |
529 |
* |
* |
530 |
* @note Chained pbufs with different reference counts should really |
* @note the reference counter of a pbuf equals the number of pointers |
531 |
* not occur. Something that references to the first pbuf, has access |
* that refer to the pbuf (or into the pbuf). |
532 |
* to the complete chain, so all references |
* |
533 |
|
* @internal examples: |
534 |
|
* |
535 |
|
* 1->2->3 becomes ...1->3 |
536 |
|
* 3->3->3 becomes 2->3->3 |
537 |
|
* 1->1->2 becomes ....->1 |
538 |
|
* 2->1->1 becomes 1->1->1 |
539 |
|
* 1->1->1 becomes ....... |
540 |
|
* |
541 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
542 |
u8_t |
u8_t |
543 |
pbuf_free(struct pbuf *p) |
pbuf_free(struct pbuf *p) |
544 |
{ |
{ |
545 |
struct pbuf *q; |
struct pbuf *q; |
546 |
u8_t count = 0; |
u8_t count; |
547 |
SYS_ARCH_DECL_PROTECT(old_level); |
SYS_ARCH_DECL_PROTECT(old_level); |
548 |
|
|
549 |
if (p == NULL) { |
if (p == NULL) { |
558 |
p->flags == PBUF_FLAG_RAM || |
p->flags == PBUF_FLAG_RAM || |
559 |
p->flags == PBUF_FLAG_REF ); |
p->flags == PBUF_FLAG_REF ); |
560 |
|
|
561 |
LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); |
q = p; |
562 |
p->ref--; |
count = 0; |
563 |
|
/* Since decrementing ref cannot be guaranteed to be a single machine operation |
|
/* Since decrementing ref cannot be guarranteed to be a single machine operation |
|
564 |
* 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. |
565 |
*/ |
*/ |
566 |
SYS_ARCH_PROTECT(old_level); |
SYS_ARCH_PROTECT(old_level); |
567 |
/* decrement individual reference count for each pbuf in chain */ |
/* de-allocate all consecutive pbufs from the head of the chain that |
568 |
for (q = p->next; q != NULL; q = q->next) { |
* obtain a zero reference count */ |
569 |
/* reference counts can be 0, as 2nd and further pbufs will |
while (p != NULL) { |
570 |
only be freed if the head of the chain is freed */ |
/* all pbufs in a chain are referenced at least once */ |
571 |
LWIP_ASSERT("pbuf_free: q->ref >= 0", q->ref >= 0); |
LWIP_ASSERT("pbuf_free: q->ref > 0", q->ref > 0); |
572 |
/* decrease reference count, but do not wrap! */ |
p->ref--; |
573 |
if (q->ref > 0) |
/* this pbuf is no longer referenced to? */ |
574 |
q->ref--; |
if (p->ref == 0) |
575 |
} |
{ |
576 |
|
/* remember next pbuf in chain for next iteration */ |
|
/* first pbuf now no longer needed? */ |
|
|
if (p->ref == 0) { |
|
|
SYS_ARCH_UNPROTECT(old_level); |
|
|
|
|
|
while (p != NULL) { |
|
|
/* remember next in chain */ |
|
577 |
q = p->next; |
q = p->next; |
578 |
/* this is a pbuf from the pool? */ |
|
579 |
|
/* is this a pbuf from the pool? */ |
580 |
if (p->flags == PBUF_FLAG_POOL) { |
if (p->flags == PBUF_FLAG_POOL) { |
581 |
p->len = p->tot_len = PBUF_POOL_BUFSIZE; |
p->len = p->tot_len = PBUF_POOL_BUFSIZE; |
582 |
p->payload = (void *)((u8_t *)p + sizeof(struct pbuf)); |
p->payload = (void *)((u8_t *)p + sizeof(struct pbuf)); |
583 |
PBUF_POOL_FREE(p); |
PBUF_POOL_FREE(p); |
584 |
/* RAM/ROM referencing pbuf */ |
/* a RAM/ROM referencing pbuf */ |
585 |
} else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) { |
} else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) { |
586 |
memp_freep(MEMP_PBUF, p); |
memp_freep(MEMP_PBUF, p); |
587 |
/* pbuf with data */ |
/* pbuf with data */ |
588 |
} else { |
} else { |
589 |
mem_free(p); |
mem_free(p); |
590 |
} |
} |
591 |
/* next in chain */ |
count++; |
592 |
|
/* proceed to next pbuf */ |
593 |
p = q; |
p = q; |
594 |
/* Only free the next one in a chain if it's reference count is 0. |
/* p->ref > 0, this pbuf is still referenced to */ |
595 |
This allows buffer chains to have multiple headers pointing to them. */ |
/* (so the remaining pbufs in chain as well) */ |
596 |
if (p != NULL) |
} else { |
597 |
{ |
/* stop walking through chain */ |
598 |
p->ref--; |
p = NULL; |
|
if (p->ref > 0) |
|
|
break; |
|
|
} |
|
|
|
|
|
++count; |
|
599 |
} |
} |
600 |
pbuf_refresh(); |
} |
601 |
} else |
SYS_ARCH_UNPROTECT(old_level); |
602 |
SYS_ARCH_UNPROTECT(old_level); |
pbuf_refresh(); |
603 |
PERF_STOP("pbuf_free"); |
PERF_STOP("pbuf_free"); |
604 |
|
/* return number of de-allocated pbufs */ |
605 |
return count; |
return count; |
606 |
} |
} |
607 |
/*-----------------------------------------------------------------------------------*/ |
|
608 |
/* pbuf_clen(): |
/** |
609 |
|
* Count number of pbufs in a chain |
610 |
* |
* |
611 |
* Returns the length of the pbuf chain. |
* @param p first pbuf of chain |
612 |
|
* @return the number of pbufs in a chain |
613 |
*/ |
*/ |
614 |
/*-----------------------------------------------------------------------------------*/ |
|
615 |
u8_t |
u8_t |
616 |
pbuf_clen(struct pbuf *p) |
pbuf_clen(struct pbuf *p) |
617 |
{ |
{ |
618 |
u8_t len; |
u8_t len; |
619 |
|
|
620 |
if(p == NULL) { |
len = 0; |
621 |
return 0; |
while (p != NULL) { |
|
} |
|
|
|
|
|
for(len = 0; p != NULL; p = p->next) { |
|
622 |
++len; |
++len; |
623 |
|
p = p->next; |
624 |
} |
} |
625 |
return len; |
return len; |
626 |
} |
} |
627 |
/*-----------------------------------------------------------------------------------*/ |
/** |
628 |
/* pbuf_ref(): |
* |
629 |
|
* Increment the reference count of the pbuf. |
630 |
|
* |
631 |
|
* @param p pbuf to increase reference counter of |
632 |
* |
* |
|
* Increments the reference count of the pbuf. |
|
633 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
634 |
void |
void |
635 |
pbuf_ref(struct pbuf *p) |
pbuf_ref(struct pbuf *p) |
636 |
{ |
{ |
637 |
SYS_ARCH_DECL_PROTECT(old_level); |
SYS_ARCH_DECL_PROTECT(old_level); |
638 |
|
/* pbuf given? */ |
639 |
if(p == NULL) { |
if(p != NULL) { |
640 |
return; |
SYS_ARCH_PROTECT(old_level); |
641 |
|
++(p->ref); |
642 |
|
SYS_ARCH_UNPROTECT(old_level); |
643 |
} |
} |
|
|
|
|
SYS_ARCH_PROTECT(old_level); |
|
|
++(p->ref); |
|
|
SYS_ARCH_UNPROTECT(old_level); |
|
644 |
} |
} |
645 |
|
|
646 |
/*------------------------------------------------------------------------------*/ |
/** |
647 |
/* pbuf_ref_chain(): |
* |
648 |
|
* Increment the reference count of all pbufs in a chain. |
649 |
|
* |
650 |
|
* @param p first pbuf of chain |
651 |
* |
* |
|
* Increments the reference count of all pbufs in a chain. |
|
652 |
*/ |
*/ |
653 |
void |
void |
654 |
pbuf_ref_chain(struct pbuf *p) |
pbuf_ref_chain(struct pbuf *p) |
655 |
{ |
{ |
656 |
SYS_ARCH_DECL_PROTECT(old_level); |
SYS_ARCH_DECL_PROTECT(old_level); |
657 |
SYS_ARCH_PROTECT(old_level); |
SYS_ARCH_PROTECT(old_level); |
658 |
|
|
659 |
while (p != NULL) { |
while (p != NULL) { |
660 |
p->ref++; |
++p->ref; |
661 |
p=p->next; |
p = p->next; |
662 |
} |
} |
663 |
|
SYS_ARCH_UNPROTECT(old_level); |
|
SYS_ARCH_UNPROTECT(old_level); |
|
664 |
} |
} |
665 |
|
|
666 |
|
|
670 |
* |
* |
671 |
* The ->tot_len field of the first pbuf (h) is adjusted. |
* The ->tot_len field of the first pbuf (h) is adjusted. |
672 |
*/ |
*/ |
|
/*-----------------------------------------------------------------------------------*/ |
|
673 |
void |
void |
674 |
pbuf_chain(struct pbuf *h, struct pbuf *t) |
pbuf_chain(struct pbuf *h, struct pbuf *t) |
675 |
{ |
{ |
679 |
LWIP_ASSERT("t != NULL", t != NULL); |
LWIP_ASSERT("t != NULL", t != NULL); |
680 |
|
|
681 |
/* proceed to last pbuf of chain */ |
/* proceed to last pbuf of chain */ |
682 |
for(p = h; p->next != NULL; p = p->next) { |
for (p = h; p->next != NULL; p = p->next) { |
683 |
/* add length of second chain to totals of first chain */ |
/* add length of second chain to totals of first chain */ |
684 |
p->tot_len += t->tot_len; |
p->tot_len += t->tot_len; |
685 |
} |
} |
686 |
/* chain */ |
/* chain last pbuf of h chain (p) with first of tail (t) */ |
687 |
p->next = t; |
p->next = t; |
|
#if PBUF_CHAIN_DOES_REFER /** TODO (WORK IN PROGRESS) */ |
|
688 |
/* t is now referenced to one more time */ |
/* t is now referenced to one more time */ |
689 |
pbuf_ref(t); |
pbuf_ref(t); |
690 |
DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_chain: referencing %p\n", (void *) t)); |
DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_chain: referencing tail %p\n", (void *) t)); |
|
#endif |
|
691 |
} |
} |
692 |
|
|
693 |
/** |
/** |
695 |
* |
* |
696 |
* Makes p->tot_len field equal to p->len. |
* Makes p->tot_len field equal to p->len. |
697 |
* @param p pbuf to dechain |
* @param p pbuf to dechain |
698 |
* @return remainder (if any) of the pbuf chain. |
* @return remainder of the pbuf chain, or NULL if it was de-allocated. |
699 |
*/ |
*/ |
700 |
struct pbuf * |
struct pbuf * |
701 |
pbuf_dechain(struct pbuf *p) |
pbuf_dechain(struct pbuf *p) |
702 |
{ |
{ |
703 |
struct pbuf *q; |
struct pbuf *q; |
704 |
|
/* tail */ |
705 |
q = p->next; |
q = p->next; |
706 |
/* pbuf has successor in chain? */ |
/* pbuf has successor in chain? */ |
707 |
if (q != NULL) { |
if (q != NULL) { |
715 |
p->next = NULL; |
p->next = NULL; |
716 |
#if PBUF_CHAIN_DOES_REFER /** TODO (WORK IN PROGRESS) */ |
#if PBUF_CHAIN_DOES_REFER /** TODO (WORK IN PROGRESS) */ |
717 |
/* q is no longer referenced by p */ |
/* q is no longer referenced by p */ |
718 |
pbuf_free(q); |
deallocated = pbuf_free(q); |
719 |
DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dechain: unreferencing %p\n", (void *) q)); |
DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dechain: unreferencing %p\n", (void *) q)); |
720 |
#endif |
#endif |
721 |
return q; |
/* return remaining tail or NULL if deallocated */ |
722 |
|
return (deallocated? NULL: q); |
723 |
} |
} |
724 |
|
|
725 |
/** |
/** |
730 |
* with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of |
* with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of |
731 |
* the referenced data. |
* the referenced data. |
732 |
* |
* |
733 |
* Used to queue packets on behalf of the lwIP stack, such as ARP based |
* @note The pbuf you give as argument, may have been replaced |
734 |
* queueing. |
* by calling pbuf_take(p). You must therefore explicitly use |
735 |
|
* p = pbuf_take(p); |
736 |
|
* @note Any replaced pbufs will be freed through pbuf_free(). |
737 |
|
* |
738 |
|
* Used to queue packets on behalf of the lwIP stack, such as |
739 |
|
* ARP based queueing. |
740 |
* |
* |
741 |
* @param f Head of pbuf chain to process |
* @param f Head of pbuf chain to process |
742 |
* |
* |
743 |
* @return Pointer to new head of pbuf chain. |
* @return Pointer to new head of pbuf chain (which may have been |
744 |
|
* replaced itself). |
745 |
*/ |
*/ |
746 |
struct pbuf * |
struct pbuf * |
747 |
pbuf_take(struct pbuf *f) |
pbuf_take(struct pbuf *f) |
748 |
{ |
{ |
749 |
struct pbuf *p, *prev, *top; |
struct pbuf *p, *prev, *top; |
750 |
LWIP_ASSERT("pbuf_take: f != NULL", f != NULL); |
LWIP_ASSERT("pbuf_take: f != NULL", f != NULL); |
751 |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)f)); |
DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)", (void*)f)); |
752 |
|
|
753 |
prev = NULL; |
prev = NULL; |
754 |
p = f; |
p = f; |
761 |
{ |
{ |
762 |
/* the replacement pbuf */ |
/* the replacement pbuf */ |
763 |
struct pbuf *q; |
struct pbuf *q; |
764 |
q = NULL; |
DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p", (void *)p)); |
|
DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p)); |
|
765 |
/* allocate a pbuf (w/ payload) fully in RAM */ |
/* allocate a pbuf (w/ payload) fully in RAM */ |
766 |
/* PBUF_POOL buffers are faster if we can use them */ |
/* PBUF_POOL buffers are faster if we can use them */ |
767 |
if (p->len <= PBUF_POOL_BUFSIZE) { |
if (p->len <= PBUF_POOL_BUFSIZE) { |
768 |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); |
769 |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAW\n")); |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL")); |
770 |
|
} else { |
771 |
|
/* no replacement pbuf yet */ |
772 |
|
q = NULL; |
773 |
|
DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF")); |
774 |
} |
} |
775 |
/* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ |
/* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ |
776 |
if (q == NULL) { |
if (q == NULL) { |
777 |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); |
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); |
778 |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n")); |
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM")); |
779 |
} |
} |
780 |
|
/* replacement pbuf could be allocated? */ |
781 |
if (q != NULL) |
if (q != NULL) |
782 |
{ |
{ |
783 |
/* copy successor */ |
/* copy successor */ |
784 |
q->next = p->next; |
q->next = p->next; |
785 |
|
/* remove linkage from original pbuf */ |
786 |
|
p->next = NULL; |
787 |
|
/* remove linkage to original pbuf */ |
788 |
if (prev != NULL) |
if (prev != NULL) |
789 |
/* Break chain and insert new pbuf instead */ |
/* prev->next == p at this point */ |
790 |
|
/* break chain and insert new pbuf instead */ |
791 |
prev->next = q; |
prev->next = q; |
792 |
|
/* p is no longer pointed to by prev or by our caller, |
793 |
|
* as the caller must do p = pbuf_take(p); so free it |
794 |
|
* from our usage. |
795 |
|
* note that we have set p->next to NULL already so that |
796 |
|
* we will not free the rest of the chain by accident. |
797 |
|
*/ |
798 |
|
pbuf_free(p); |
799 |
|
/* prev == NULL, so we replaced the top pbuf of the chain */ |
800 |
else |
else |
801 |
top = q; |
top = q; |
|
p->next = NULL; |
|
802 |
/* copy pbuf payload */ |
/* copy pbuf payload */ |
803 |
memcpy(q->payload, p->payload, p->len); |
memcpy(q->payload, p->payload, p->len); |
804 |
q->tot_len = p->tot_len; |
q->tot_len = p->tot_len; |