Travis Geiselbrecht | 1d0df69 | 2008-09-01 02:26:09 -0700 | [diff] [blame^] | 1 | /** |
| 2 | * @file |
| 3 | * Packet buffer management |
| 4 | * |
| 5 | * Packets are built from the pbuf data structure. It supports dynamic |
| 6 | * memory allocation for packet contents or can reference externally |
| 7 | * managed packet contents both in RAM and ROM. Quick allocation for |
| 8 | * incoming packets is provided through pools with fixed sized pbufs. |
| 9 | * |
| 10 | * A packet may span over multiple pbufs, chained as a singly linked |
| 11 | * list. This is called a "pbuf chain". |
| 12 | * |
| 13 | * Multiple packets may be queued, also using this singly linked list. |
| 14 | * This is called a "packet queue". |
| 15 | * |
| 16 | * So, a packet queue consists of one or more pbuf chains, each of |
| 17 | * which consist of one or more pbufs. Currently, queues are only |
| 18 | * supported in a limited section of lwIP, this is the etharp queueing |
| 19 | * code. Outside of this section no packet queues are supported yet. |
| 20 | * |
| 21 | * The differences between a pbuf chain and a packet queue are very |
| 22 | * precise but subtle. |
| 23 | * |
| 24 | * The last pbuf of a packet has a ->tot_len field that equals the |
| 25 | * ->len field. It can be found by traversing the list. If the last |
| 26 | * pbuf of a packet has a ->next field other than NULL, more packets |
| 27 | * are on the queue. |
| 28 | * |
| 29 | * Therefore, looping through a pbuf of a single packet, has an |
| 30 | * loop end condition (tot_len == p->len), NOT (next == NULL). |
| 31 | */ |
| 32 | |
| 33 | /* |
| 34 | * Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
| 35 | * All rights reserved. |
| 36 | * |
| 37 | * Redistribution and use in source and binary forms, with or without modification, |
| 38 | * are permitted provided that the following conditions are met: |
| 39 | * |
| 40 | * 1. Redistributions of source code must retain the above copyright notice, |
| 41 | * this list of conditions and the following disclaimer. |
| 42 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
| 43 | * this list of conditions and the following disclaimer in the documentation |
| 44 | * and/or other materials provided with the distribution. |
| 45 | * 3. The name of the author may not be used to endorse or promote products |
| 46 | * derived from this software without specific prior written permission. |
| 47 | * |
| 48 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 49 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 50 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
| 51 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 52 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
| 53 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 54 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 55 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| 56 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
| 57 | * OF SUCH DAMAGE. |
| 58 | * |
| 59 | * This file is part of the lwIP TCP/IP stack. |
| 60 | * |
| 61 | * Author: Adam Dunkels <adam@sics.se> |
| 62 | * |
| 63 | */ |
| 64 | |
| 65 | #include <string.h> |
| 66 | |
| 67 | #include "lwip/opt.h" |
| 68 | #include "lwip/stats.h" |
| 69 | #include "lwip/def.h" |
| 70 | #include "lwip/mem.h" |
| 71 | #include "lwip/memp.h" |
| 72 | #include "lwip/pbuf.h" |
| 73 | #include "lwip/sys.h" |
| 74 | #include "arch/perf.h" |
| 75 | |
| 76 | static u8_t pbuf_pool_memory[MEM_ALIGNMENT - 1 + PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf))]; |
| 77 | |
| 78 | #if !SYS_LIGHTWEIGHT_PROT |
| 79 | static volatile u8_t pbuf_pool_free_lock, pbuf_pool_alloc_lock; |
| 80 | static sys_sem_t pbuf_pool_free_sem; |
| 81 | #endif |
| 82 | |
| 83 | static struct pbuf *pbuf_pool = NULL; |
| 84 | |
| 85 | /** |
| 86 | * Initializes the pbuf module. |
| 87 | * |
| 88 | * A large part of memory is allocated for holding the pool of pbufs. |
| 89 | * The size of the individual pbufs in the pool is given by the size |
| 90 | * parameter, and the number of pbufs in the pool by the num parameter. |
| 91 | * |
| 92 | * After the memory has been allocated, the pbufs are set up. The |
| 93 | * ->next pointer in each pbuf is set up to point to the next pbuf in |
| 94 | * the pool. |
| 95 | * |
| 96 | */ |
| 97 | void |
| 98 | pbuf_init(void) |
| 99 | { |
| 100 | struct pbuf *p, *q = NULL; |
| 101 | u16_t i; |
| 102 | |
| 103 | pbuf_pool = (struct pbuf *)MEM_ALIGN(pbuf_pool_memory); |
| 104 | |
| 105 | #if PBUF_STATS |
| 106 | lwip_stats.pbuf.avail = PBUF_POOL_SIZE; |
| 107 | #endif /* PBUF_STATS */ |
| 108 | |
| 109 | /* Set up ->next pointers to link the pbufs of the pool together */ |
| 110 | p = pbuf_pool; |
| 111 | |
| 112 | for(i = 0; i < PBUF_POOL_SIZE; ++i) { |
| 113 | p->next = (struct pbuf *)((u8_t *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf)); |
| 114 | p->len = p->tot_len = PBUF_POOL_BUFSIZE; |
| 115 | p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf))); |
| 116 | p->flags = PBUF_FLAG_POOL; |
| 117 | q = p; |
| 118 | p = p->next; |
| 119 | } |
| 120 | |
| 121 | /* The ->next pointer of last pbuf is NULL to indicate that there |
| 122 | are no more pbufs in the pool */ |
| 123 | q->next = NULL; |
| 124 | |
| 125 | #if !SYS_LIGHTWEIGHT_PROT |
| 126 | pbuf_pool_alloc_lock = 0; |
| 127 | pbuf_pool_free_lock = 0; |
| 128 | pbuf_pool_free_sem = sys_sem_new(1); |
| 129 | #endif |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * @internal only called from pbuf_alloc() |
| 134 | */ |
| 135 | static struct pbuf * |
| 136 | pbuf_pool_alloc(void) |
| 137 | { |
| 138 | struct pbuf *p = NULL; |
| 139 | |
| 140 | SYS_ARCH_DECL_PROTECT(old_level); |
| 141 | SYS_ARCH_PROTECT(old_level); |
| 142 | |
| 143 | #if !SYS_LIGHTWEIGHT_PROT |
| 144 | /* Next, check the actual pbuf pool, but if the pool is locked, we |
| 145 | pretend to be out of buffers and return NULL. */ |
| 146 | if (pbuf_pool_free_lock) { |
| 147 | #if PBUF_STATS |
| 148 | ++lwip_stats.pbuf.alloc_locked; |
| 149 | #endif /* PBUF_STATS */ |
| 150 | return NULL; |
| 151 | } |
| 152 | pbuf_pool_alloc_lock = 1; |
| 153 | if (!pbuf_pool_free_lock) { |
| 154 | #endif /* SYS_LIGHTWEIGHT_PROT */ |
| 155 | p = pbuf_pool; |
| 156 | if (p) { |
| 157 | pbuf_pool = p->next; |
| 158 | } |
| 159 | #if !SYS_LIGHTWEIGHT_PROT |
| 160 | #if PBUF_STATS |
| 161 | } else { |
| 162 | ++lwip_stats.pbuf.alloc_locked; |
| 163 | #endif /* PBUF_STATS */ |
| 164 | } |
| 165 | pbuf_pool_alloc_lock = 0; |
| 166 | #endif /* SYS_LIGHTWEIGHT_PROT */ |
| 167 | |
| 168 | #if PBUF_STATS |
| 169 | if (p != NULL) { |
| 170 | ++lwip_stats.pbuf.used; |
| 171 | if (lwip_stats.pbuf.used > lwip_stats.pbuf.max) { |
| 172 | lwip_stats.pbuf.max = lwip_stats.pbuf.used; |
| 173 | } |
| 174 | } |
| 175 | #endif /* PBUF_STATS */ |
| 176 | |
| 177 | SYS_ARCH_UNPROTECT(old_level); |
| 178 | return p; |
| 179 | } |
| 180 | |
| 181 | |
| 182 | /** |
| 183 | * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). |
| 184 | * |
| 185 | * The actual memory allocated for the pbuf is determined by the |
| 186 | * layer at which the pbuf is allocated and the requested size |
| 187 | * (from the size parameter). |
| 188 | * |
| 189 | * @param flag this parameter decides how and where the pbuf |
| 190 | * should be allocated as follows: |
| 191 | * |
| 192 | * - PBUF_RAM: buffer memory for pbuf is allocated as one large |
| 193 | * chunk. This includes protocol headers as well. |
| 194 | * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for |
| 195 | * protocol headers. Additional headers must be prepended |
| 196 | * by allocating another pbuf and chain in to the front of |
| 197 | * the ROM pbuf. It is assumed that the memory used is really |
| 198 | * similar to ROM in that it is immutable and will not be |
| 199 | * changed. Memory which is dynamic should generally not |
| 200 | * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. |
| 201 | * - PBUF_REF: no buffer memory is allocated for the pbuf, even for |
| 202 | * protocol headers. It is assumed that the pbuf is only |
| 203 | * being used in a single thread. If the pbuf gets queued, |
| 204 | * then pbuf_take should be called to copy the buffer. |
| 205 | * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from |
| 206 | * the pbuf pool that is allocated during pbuf_init(). |
| 207 | * |
| 208 | * @return the allocated pbuf. If multiple pbufs where allocated, this |
| 209 | * is the first pbuf of a pbuf chain. |
| 210 | */ |
| 211 | struct pbuf * |
| 212 | pbuf_alloc(pbuf_layer l, u16_t length, pbuf_flag flag) |
| 213 | { |
| 214 | struct pbuf *p, *q, *r; |
| 215 | u16_t offset; |
| 216 | s32_t rem_len; /* remaining length */ |
| 217 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F")\n", length)); |
| 218 | |
| 219 | /* determine header offset */ |
| 220 | offset = 0; |
| 221 | switch (l) { |
| 222 | case PBUF_TRANSPORT: |
| 223 | /* add room for transport (often TCP) layer header */ |
| 224 | offset += PBUF_TRANSPORT_HLEN; |
| 225 | /* FALLTHROUGH */ |
| 226 | case PBUF_IP: |
| 227 | /* add room for IP layer header */ |
| 228 | offset += PBUF_IP_HLEN; |
| 229 | /* FALLTHROUGH */ |
| 230 | case PBUF_LINK: |
| 231 | /* add room for link layer header */ |
| 232 | offset += PBUF_LINK_HLEN; |
| 233 | break; |
| 234 | case PBUF_RAW: |
| 235 | break; |
| 236 | default: |
| 237 | LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); |
| 238 | return NULL; |
| 239 | } |
| 240 | |
| 241 | switch (flag) { |
| 242 | case PBUF_POOL: |
| 243 | /* allocate head of pbuf chain into p */ |
| 244 | p = pbuf_pool_alloc(); |
| 245 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); |
| 246 | if (p == NULL) { |
| 247 | #if PBUF_STATS |
| 248 | ++lwip_stats.pbuf.err; |
| 249 | #endif /* PBUF_STATS */ |
| 250 | return NULL; |
| 251 | } |
| 252 | p->next = NULL; |
| 253 | |
| 254 | /* make the payload pointer point 'offset' bytes into pbuf data memory */ |
| 255 | p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset))); |
| 256 | LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", |
| 257 | ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); |
| 258 | /* the total length of the pbuf chain is the requested size */ |
| 259 | p->tot_len = length; |
| 260 | /* set the length of the first pbuf in the chain */ |
| 261 | p->len = length > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: length; |
| 262 | /* set reference count (needed here in case we fail) */ |
| 263 | p->ref = 1; |
| 264 | |
| 265 | /* now allocate the tail of the pbuf chain */ |
| 266 | |
| 267 | /* remember first pbuf for linkage in next iteration */ |
| 268 | r = p; |
| 269 | /* remaining length to be allocated */ |
| 270 | rem_len = length - p->len; |
| 271 | /* any remaining pbufs to be allocated? */ |
| 272 | while (rem_len > 0) { |
| 273 | q = pbuf_pool_alloc(); |
| 274 | if (q == NULL) { |
| 275 | LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n")); |
| 276 | #if PBUF_STATS |
| 277 | ++lwip_stats.pbuf.err; |
| 278 | #endif /* PBUF_STATS */ |
| 279 | /* free chain so far allocated */ |
| 280 | pbuf_free(p); |
| 281 | /* bail out unsuccesfully */ |
| 282 | return NULL; |
| 283 | } |
| 284 | q->next = NULL; |
| 285 | /* make previous pbuf point to this pbuf */ |
| 286 | r->next = q; |
| 287 | /* set total length of this pbuf and next in chain */ |
| 288 | q->tot_len = rem_len; |
| 289 | /* this pbuf length is pool size, unless smaller sized tail */ |
| 290 | q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len; |
| 291 | q->payload = (void *)((u8_t *)q + sizeof(struct pbuf)); |
| 292 | LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", |
| 293 | ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); |
| 294 | q->ref = 1; |
| 295 | /* calculate remaining length to be allocated */ |
| 296 | rem_len -= q->len; |
| 297 | /* remember this pbuf for linkage in next iteration */ |
| 298 | r = q; |
| 299 | } |
| 300 | /* end of chain */ |
| 301 | /*r->next = NULL;*/ |
| 302 | |
| 303 | break; |
| 304 | case PBUF_RAM: |
| 305 | /* If pbuf is to be allocated in RAM, allocate memory for it. */ |
| 306 | p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + offset) + MEM_ALIGN_SIZE(length)); |
| 307 | if (p == NULL) { |
| 308 | return NULL; |
| 309 | } |
| 310 | /* Set up internal structure of the pbuf. */ |
| 311 | p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset)); |
| 312 | p->len = p->tot_len = length; |
| 313 | p->next = NULL; |
| 314 | p->flags = PBUF_FLAG_RAM; |
| 315 | |
| 316 | LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", |
| 317 | ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); |
| 318 | break; |
| 319 | /* pbuf references existing (non-volatile static constant) ROM payload? */ |
| 320 | case PBUF_ROM: |
| 321 | /* pbuf references existing (externally allocated) RAM payload? */ |
| 322 | case PBUF_REF: |
| 323 | /* only allocate memory for the pbuf structure */ |
| 324 | p = memp_malloc(MEMP_PBUF); |
| 325 | if (p == NULL) { |
| 326 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", flag == PBUF_ROM?"ROM":"REF")); |
| 327 | return NULL; |
| 328 | } |
| 329 | /* caller must set this field properly, afterwards */ |
| 330 | p->payload = NULL; |
| 331 | p->len = p->tot_len = length; |
| 332 | p->next = NULL; |
| 333 | p->flags = (flag == PBUF_ROM? PBUF_FLAG_ROM: PBUF_FLAG_REF); |
| 334 | break; |
| 335 | default: |
| 336 | LWIP_ASSERT("pbuf_alloc: erroneous flag", 0); |
| 337 | return NULL; |
| 338 | } |
| 339 | /* set reference count */ |
| 340 | p->ref = 1; |
| 341 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); |
| 342 | return p; |
| 343 | } |
| 344 | |
| 345 | |
| 346 | #if PBUF_STATS |
| 347 | #define DEC_PBUF_STATS do { --lwip_stats.pbuf.used; } while (0) |
| 348 | #else /* PBUF_STATS */ |
| 349 | #define DEC_PBUF_STATS |
| 350 | #endif /* PBUF_STATS */ |
| 351 | |
| 352 | #define PBUF_POOL_FAST_FREE(p) do { \ |
| 353 | p->next = pbuf_pool; \ |
| 354 | pbuf_pool = p; \ |
| 355 | DEC_PBUF_STATS; \ |
| 356 | } while (0) |
| 357 | |
| 358 | #if SYS_LIGHTWEIGHT_PROT |
| 359 | #define PBUF_POOL_FREE(p) do { \ |
| 360 | SYS_ARCH_DECL_PROTECT(old_level); \ |
| 361 | SYS_ARCH_PROTECT(old_level); \ |
| 362 | PBUF_POOL_FAST_FREE(p); \ |
| 363 | SYS_ARCH_UNPROTECT(old_level); \ |
| 364 | } while (0) |
| 365 | #else /* SYS_LIGHTWEIGHT_PROT */ |
| 366 | #define PBUF_POOL_FREE(p) do { \ |
| 367 | sys_sem_wait(pbuf_pool_free_sem); \ |
| 368 | PBUF_POOL_FAST_FREE(p); \ |
| 369 | sys_sem_signal(pbuf_pool_free_sem); \ |
| 370 | } while (0) |
| 371 | #endif /* SYS_LIGHTWEIGHT_PROT */ |
| 372 | |
| 373 | /** |
| 374 | * Shrink a pbuf chain to a desired length. |
| 375 | * |
| 376 | * @param p pbuf to shrink. |
| 377 | * @param new_len desired new length of pbuf chain |
| 378 | * |
| 379 | * Depending on the desired length, the first few pbufs in a chain might |
| 380 | * be skipped and left unchanged. The new last pbuf in the chain will be |
| 381 | * resized, and any remaining pbufs will be freed. |
| 382 | * |
| 383 | * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. |
| 384 | * @note May not be called on a packet queue. |
| 385 | * |
| 386 | * @bug Cannot grow the size of a pbuf (chain) (yet). |
| 387 | */ |
| 388 | void |
| 389 | pbuf_realloc(struct pbuf *p, u16_t new_len) |
| 390 | { |
| 391 | struct pbuf *q; |
| 392 | u16_t rem_len; /* remaining length */ |
| 393 | s16_t grow; |
| 394 | |
| 395 | LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL || |
| 396 | p->flags == PBUF_FLAG_ROM || |
| 397 | p->flags == PBUF_FLAG_RAM || |
| 398 | p->flags == PBUF_FLAG_REF); |
| 399 | |
| 400 | /* desired length larger than current length? */ |
| 401 | if (new_len >= p->tot_len) { |
| 402 | /* enlarging not yet supported */ |
| 403 | return; |
| 404 | } |
| 405 | |
| 406 | /* the pbuf chain grows by (new_len - p->tot_len) bytes |
| 407 | * (which may be negative in case of shrinking) */ |
| 408 | grow = new_len - p->tot_len; |
| 409 | |
| 410 | /* first, step over any pbufs that should remain in the chain */ |
| 411 | rem_len = new_len; |
| 412 | q = p; |
| 413 | /* should this pbuf be kept? */ |
| 414 | while (rem_len > q->len) { |
| 415 | /* decrease remaining length by pbuf length */ |
| 416 | rem_len -= q->len; |
| 417 | /* decrease total length indicator */ |
| 418 | q->tot_len += grow; |
| 419 | /* proceed to next pbuf in chain */ |
| 420 | q = q->next; |
| 421 | } |
| 422 | /* we have now reached the new last pbuf (in q) */ |
| 423 | /* rem_len == desired length for pbuf q */ |
| 424 | |
| 425 | /* shrink allocated memory for PBUF_RAM */ |
| 426 | /* (other types merely adjust their length fields */ |
| 427 | if ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len)) { |
| 428 | /* reallocate and adjust the length of the pbuf that will be split */ |
| 429 | mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); |
| 430 | } |
| 431 | /* adjust length fields for new last pbuf */ |
| 432 | q->len = rem_len; |
| 433 | q->tot_len = q->len; |
| 434 | |
| 435 | /* any remaining pbufs in chain? */ |
| 436 | if (q->next != NULL) { |
| 437 | /* free remaining pbufs in chain */ |
| 438 | pbuf_free(q->next); |
| 439 | } |
| 440 | /* q is last packet in chain */ |
| 441 | q->next = NULL; |
| 442 | |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * Adjusts the payload pointer to hide or reveal headers in the payload. |
| 447 | * |
| 448 | * Adjusts the ->payload pointer so that space for a header |
| 449 | * (dis)appears in the pbuf payload. |
| 450 | * |
| 451 | * The ->payload, ->tot_len and ->len fields are adjusted. |
| 452 | * |
| 453 | * @param hdr_size_inc Number of bytes to increment header size which |
| 454 | * increases the size of the pbuf. New space is on the front. |
| 455 | * (Using a negative value decreases the header size.) |
| 456 | * If hdr_size_inc is 0, this function does nothing and returns succesful. |
| 457 | * |
| 458 | * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so |
| 459 | * the call will fail. A check is made that the increase in header size does |
| 460 | * not move the payload pointer in front of the start of the buffer. |
| 461 | * @return non-zero on failure, zero on success. |
| 462 | * |
| 463 | */ |
| 464 | u8_t |
| 465 | pbuf_header(struct pbuf *p, s16_t header_size_increment) |
| 466 | { |
| 467 | void *payload; |
| 468 | |
| 469 | LWIP_ASSERT("p != NULL", p != NULL); |
| 470 | if ((header_size_increment == 0) || (p == NULL)) return 0; |
| 471 | |
| 472 | /* remember current payload pointer */ |
| 473 | payload = p->payload; |
| 474 | |
| 475 | /* pbuf types containing payloads? */ |
| 476 | if (p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_POOL) { |
| 477 | /* set new payload pointer */ |
| 478 | p->payload = (u8_t *)p->payload - header_size_increment; |
| 479 | /* boundary check fails? */ |
| 480 | if ((u8_t *)p->payload < (u8_t *)p + sizeof(struct pbuf)) { |
| 481 | LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", |
| 482 | (void *)p->payload, |
| 483 | (void *)(p + 1)));\ |
| 484 | /* restore old payload pointer */ |
| 485 | p->payload = payload; |
| 486 | /* bail out unsuccesfully */ |
| 487 | return 1; |
| 488 | } |
| 489 | /* pbuf types refering to external payloads? */ |
| 490 | } else if (p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_ROM) { |
| 491 | /* hide a header in the payload? */ |
| 492 | if ((header_size_increment < 0) && (header_size_increment - p->len <= 0)) { |
| 493 | /* increase payload pointer */ |
| 494 | p->payload = (u8_t *)p->payload - header_size_increment; |
| 495 | } else { |
| 496 | /* cannot expand payload to front (yet!) |
| 497 | * bail out unsuccesfully */ |
| 498 | return 1; |
| 499 | } |
| 500 | } |
| 501 | /* modify pbuf length fields */ |
| 502 | p->len += header_size_increment; |
| 503 | p->tot_len += header_size_increment; |
| 504 | |
| 505 | LWIP_DEBUGF( PBUF_DEBUG, ("pbuf_header: old %p new %p (%"S16_F")\n", |
| 506 | (void *)payload, (void *)p->payload, header_size_increment)); |
| 507 | |
| 508 | return 0; |
| 509 | } |
| 510 | |
| 511 | /** |
| 512 | * Dereference a pbuf chain or queue and deallocate any no-longer-used |
| 513 | * pbufs at the head of this chain or queue. |
| 514 | * |
| 515 | * Decrements the pbuf reference count. If it reaches zero, the pbuf is |
| 516 | * deallocated. |
| 517 | * |
| 518 | * For a pbuf chain, this is repeated for each pbuf in the chain, |
| 519 | * up to the first pbuf which has a non-zero reference count after |
| 520 | * decrementing. So, when all reference counts are one, the whole |
| 521 | * chain is free'd. |
| 522 | * |
| 523 | * @param pbuf The pbuf (chain) to be dereferenced. |
| 524 | * |
| 525 | * @return the number of pbufs that were de-allocated |
| 526 | * from the head of the chain. |
| 527 | * |
| 528 | * @note MUST NOT be called on a packet queue (Not verified to work yet). |
| 529 | * @note the reference counter of a pbuf equals the number of pointers |
| 530 | * that refer to the pbuf (or into the pbuf). |
| 531 | * |
| 532 | * @internal examples: |
| 533 | * |
| 534 | * Assuming existing chains a->b->c with the following reference |
| 535 | * counts, calling pbuf_free(a) results in: |
| 536 | * |
| 537 | * 1->2->3 becomes ...1->3 |
| 538 | * 3->3->3 becomes 2->3->3 |
| 539 | * 1->1->2 becomes ......1 |
| 540 | * 2->1->1 becomes 1->1->1 |
| 541 | * 1->1->1 becomes ....... |
| 542 | * |
| 543 | */ |
| 544 | u8_t |
| 545 | pbuf_free(struct pbuf *p) |
| 546 | { |
| 547 | struct pbuf *q; |
| 548 | u8_t count; |
| 549 | SYS_ARCH_DECL_PROTECT(old_level); |
| 550 | |
| 551 | LWIP_ASSERT("p != NULL", p != NULL); |
| 552 | /* if assertions are disabled, proceed with debug output */ |
| 553 | if (p == NULL) { |
| 554 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n")); |
| 555 | return 0; |
| 556 | } |
| 557 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p)); |
| 558 | |
| 559 | PERF_START; |
| 560 | |
| 561 | LWIP_ASSERT("pbuf_free: sane flags", |
| 562 | p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_ROM || |
| 563 | p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_POOL); |
| 564 | |
| 565 | count = 0; |
| 566 | /* Since decrementing ref cannot be guaranteed to be a single machine operation |
| 567 | * we must protect it. Also, the later test of ref must be protected. |
| 568 | */ |
| 569 | SYS_ARCH_PROTECT(old_level); |
| 570 | /* de-allocate all consecutive pbufs from the head of the chain that |
| 571 | * obtain a zero reference count after decrementing*/ |
| 572 | while (p != NULL) { |
| 573 | /* all pbufs in a chain are referenced at least once */ |
| 574 | LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); |
| 575 | /* decrease reference count (number of pointers to pbuf) */ |
| 576 | p->ref--; |
| 577 | /* this pbuf is no longer referenced to? */ |
| 578 | if (p->ref == 0) { |
| 579 | /* remember next pbuf in chain for next iteration */ |
| 580 | q = p->next; |
| 581 | LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p)); |
| 582 | /* is this a pbuf from the pool? */ |
| 583 | if (p->flags == PBUF_FLAG_POOL) { |
| 584 | p->len = p->tot_len = PBUF_POOL_BUFSIZE; |
| 585 | p->payload = (void *)((u8_t *)p + sizeof(struct pbuf)); |
| 586 | PBUF_POOL_FREE(p); |
| 587 | /* is this a ROM or RAM referencing pbuf? */ |
| 588 | } else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) { |
| 589 | memp_free(MEMP_PBUF, p); |
| 590 | /* p->flags == PBUF_FLAG_RAM */ |
| 591 | } else { |
| 592 | mem_free(p); |
| 593 | } |
| 594 | count++; |
| 595 | /* proceed to next pbuf */ |
| 596 | p = q; |
| 597 | /* p->ref > 0, this pbuf is still referenced to */ |
| 598 | /* (and so the remaining pbufs in chain as well) */ |
| 599 | } else { |
| 600 | LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)p->ref)); |
| 601 | /* stop walking through the chain */ |
| 602 | p = NULL; |
| 603 | } |
| 604 | } |
| 605 | SYS_ARCH_UNPROTECT(old_level); |
| 606 | PERF_STOP("pbuf_free"); |
| 607 | /* return number of de-allocated pbufs */ |
| 608 | return count; |
| 609 | } |
| 610 | |
| 611 | /** |
| 612 | * Count number of pbufs in a chain |
| 613 | * |
| 614 | * @param p first pbuf of chain |
| 615 | * @return the number of pbufs in a chain |
| 616 | */ |
| 617 | |
| 618 | u8_t |
| 619 | pbuf_clen(struct pbuf *p) |
| 620 | { |
| 621 | u8_t len; |
| 622 | |
| 623 | len = 0; |
| 624 | while (p != NULL) { |
| 625 | ++len; |
| 626 | p = p->next; |
| 627 | } |
| 628 | return len; |
| 629 | } |
| 630 | |
| 631 | /** |
| 632 | * Increment the reference count of the pbuf. |
| 633 | * |
| 634 | * @param p pbuf to increase reference counter of |
| 635 | * |
| 636 | */ |
| 637 | void |
| 638 | pbuf_ref(struct pbuf *p) |
| 639 | { |
| 640 | SYS_ARCH_DECL_PROTECT(old_level); |
| 641 | /* pbuf given? */ |
| 642 | if (p != NULL) { |
| 643 | SYS_ARCH_PROTECT(old_level); |
| 644 | ++(p->ref); |
| 645 | SYS_ARCH_UNPROTECT(old_level); |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | /** |
| 650 | * Concatenate two pbufs (each may be a pbuf chain) and take over |
| 651 | * the caller's reference of the tail pbuf. |
| 652 | * |
| 653 | * @note The caller MAY NOT reference the tail pbuf afterwards. |
| 654 | * Use pbuf_chain() for that purpose. |
| 655 | * |
| 656 | * @see pbuf_chain() |
| 657 | */ |
| 658 | |
| 659 | void |
| 660 | pbuf_cat(struct pbuf *h, struct pbuf *t) |
| 661 | { |
| 662 | struct pbuf *p; |
| 663 | |
| 664 | LWIP_ASSERT("h != NULL (programmer violates API)", h != NULL); |
| 665 | LWIP_ASSERT("t != NULL (programmer violates API)", t != NULL); |
| 666 | if ((h == NULL) || (t == NULL)) return; |
| 667 | |
| 668 | /* proceed to last pbuf of chain */ |
| 669 | for (p = h; p->next != NULL; p = p->next) { |
| 670 | /* add total length of second chain to all totals of first chain */ |
| 671 | p->tot_len += t->tot_len; |
| 672 | } |
| 673 | /* { p is last pbuf of first h chain, p->next == NULL } */ |
| 674 | LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); |
| 675 | LWIP_ASSERT("p->next == NULL", p->next == NULL); |
| 676 | /* add total length of second chain to last pbuf total of first chain */ |
| 677 | p->tot_len += t->tot_len; |
| 678 | /* chain last pbuf of head (p) with first of tail (t) */ |
| 679 | p->next = t; |
| 680 | /* p->next now references t, but the caller will drop its reference to t, |
| 681 | * so netto there is no change to the reference count of t. |
| 682 | */ |
| 683 | } |
| 684 | |
| 685 | /** |
| 686 | * Chain two pbufs (or pbuf chains) together. |
| 687 | * |
| 688 | * The caller MUST call pbuf_free(t) once it has stopped |
| 689 | * using it. Use pbuf_cat() instead if you no longer use t. |
| 690 | * |
| 691 | * @param h head pbuf (chain) |
| 692 | * @param t tail pbuf (chain) |
| 693 | * @note The pbufs MUST belong to the same packet. |
| 694 | * @note MAY NOT be called on a packet queue. |
| 695 | * |
| 696 | * The ->tot_len fields of all pbufs of the head chain are adjusted. |
| 697 | * The ->next field of the last pbuf of the head chain is adjusted. |
| 698 | * The ->ref field of the first pbuf of the tail chain is adjusted. |
| 699 | * |
| 700 | */ |
| 701 | void |
| 702 | pbuf_chain(struct pbuf *h, struct pbuf *t) |
| 703 | { |
| 704 | pbuf_cat(h, t); |
| 705 | /* t is now referenced by h */ |
| 706 | pbuf_ref(t); |
| 707 | LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); |
| 708 | } |
| 709 | |
| 710 | /* For packet queueing. Note that queued packets MUST be dequeued first |
| 711 | * using pbuf_dequeue() before calling other pbuf_() functions. */ |
| 712 | #if ARP_QUEUEING |
| 713 | /** |
| 714 | * Add a packet to the end of a queue. |
| 715 | * |
| 716 | * @param q pointer to first packet on the queue |
| 717 | * @param n packet to be queued |
| 718 | * |
| 719 | * Both packets MUST be given, and must be different. |
| 720 | */ |
| 721 | void |
| 722 | pbuf_queue(struct pbuf *p, struct pbuf *n) |
| 723 | { |
| 724 | #if PBUF_DEBUG /* remember head of queue */ |
| 725 | struct pbuf *q = p; |
| 726 | #endif |
| 727 | /* programmer stupidity checks */ |
| 728 | LWIP_ASSERT("p == NULL in pbuf_queue: this indicates a programmer error\n", p != NULL); |
| 729 | LWIP_ASSERT("n == NULL in pbuf_queue: this indicates a programmer error\n", n != NULL); |
| 730 | LWIP_ASSERT("p == n in pbuf_queue: this indicates a programmer error\n", p != n); |
| 731 | if ((p == NULL) || (n == NULL) || (p == n)){ |
| 732 | LWIP_DEBUGF(PBUF_DEBUG | DBG_HALT | 3, ("pbuf_queue: programmer argument error\n")); |
| 733 | return; |
| 734 | } |
| 735 | |
| 736 | /* iterate through all packets on queue */ |
| 737 | while (p->next != NULL) { |
| 738 | /* be very picky about pbuf chain correctness */ |
| 739 | #if PBUF_DEBUG |
| 740 | /* iterate through all pbufs in packet */ |
| 741 | while (p->tot_len != p->len) { |
| 742 | /* make sure invariant condition holds */ |
| 743 | LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); |
| 744 | /* make sure each packet is complete */ |
| 745 | LWIP_ASSERT("p->next != NULL", p->next != NULL); |
| 746 | p = p->next; |
| 747 | /* { p->tot_len == p->len => p is last pbuf of a packet } */ |
| 748 | } |
| 749 | /* { p is last pbuf of a packet } */ |
| 750 | /* proceed to next packet on queue */ |
| 751 | #endif |
| 752 | /* proceed to next pbuf */ |
| 753 | if (p->next != NULL) p = p->next; |
| 754 | } |
| 755 | /* { p->tot_len == p->len and p->next == NULL } ==> |
| 756 | * { p is last pbuf of last packet on queue } */ |
| 757 | /* chain last pbuf of queue with n */ |
| 758 | p->next = n; |
| 759 | /* n is now referenced to by the (packet p in the) queue */ |
| 760 | pbuf_ref(n); |
| 761 | #if PBUF_DEBUG |
| 762 | LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, |
| 763 | ("pbuf_queue: newly queued packet %p sits after packet %p in queue %p\n", |
| 764 | (void *)n, (void *)p, (void *)q)); |
| 765 | #endif |
| 766 | } |
| 767 | |
| 768 | /** |
| 769 | * Remove a packet from the head of a queue. |
| 770 | * |
| 771 | * The caller MUST reference the remainder of the queue (as returned). The |
| 772 | * caller MUST NOT call pbuf_ref() as it implicitly takes over the reference |
| 773 | * from p. |
| 774 | * |
| 775 | * @param p pointer to first packet on the queue which will be dequeued. |
| 776 | * @return first packet on the remaining queue (NULL if no further packets). |
| 777 | * |
| 778 | */ |
| 779 | struct pbuf * |
| 780 | pbuf_dequeue(struct pbuf *p) |
| 781 | { |
| 782 | struct pbuf *q; |
| 783 | LWIP_ASSERT("p != NULL", p != NULL); |
| 784 | |
| 785 | /* iterate through all pbufs in packet p */ |
| 786 | while (p->tot_len != p->len) { |
| 787 | /* make sure invariant condition holds */ |
| 788 | LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); |
| 789 | /* make sure each packet is complete */ |
| 790 | LWIP_ASSERT("p->next != NULL", p->next != NULL); |
| 791 | p = p->next; |
| 792 | } |
| 793 | /* { p->tot_len == p->len } => p is the last pbuf of the first packet */ |
| 794 | /* remember next packet on queue in q */ |
| 795 | q = p->next; |
| 796 | /* dequeue packet p from queue */ |
| 797 | p->next = NULL; |
| 798 | /* any next packet on queue? */ |
| 799 | if (q != NULL) { |
| 800 | /* although q is no longer referenced by p, it MUST be referenced by |
| 801 | * the caller, who is maintaining this packet queue. So, we do not call |
| 802 | * pbuf_free(q) here, resulting in an implicit pbuf_ref(q) for the caller. */ |
| 803 | LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: first remaining packet on queue is %p\n", (void *)q)); |
| 804 | } else { |
| 805 | LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: no further packets on queue\n")); |
| 806 | } |
| 807 | return q; |
| 808 | } |
| 809 | #endif |
| 810 | |
| 811 | /** |
| 812 | * |
| 813 | * Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs. |
| 814 | * |
| 815 | * Used to queue packets on behalf of the lwIP stack, such as |
| 816 | * ARP based queueing. |
| 817 | * |
| 818 | * Go through a pbuf chain and replace any PBUF_REF buffers |
| 819 | * with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of |
| 820 | * the referenced data. |
| 821 | * |
| 822 | * @note You MUST explicitly use p = pbuf_take(p); |
| 823 | * The pbuf you give as argument, may have been replaced |
| 824 | * by a (differently located) copy through pbuf_take()! |
| 825 | * |
| 826 | * @note Any replaced pbufs will be freed through pbuf_free(). |
| 827 | * This may deallocate them if they become no longer referenced. |
| 828 | * |
| 829 | * @param p Head of pbuf chain to process |
| 830 | * |
| 831 | * @return Pointer to head of pbuf chain |
| 832 | */ |
| 833 | struct pbuf * |
| 834 | pbuf_take(struct pbuf *p) |
| 835 | { |
| 836 | struct pbuf *q , *prev, *head; |
| 837 | LWIP_ASSERT("pbuf_take: p != NULL\n", p != NULL); |
| 838 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)p)); |
| 839 | |
| 840 | prev = NULL; |
| 841 | head = p; |
| 842 | /* iterate through pbuf chain */ |
| 843 | do |
| 844 | { |
| 845 | /* pbuf is of type PBUF_REF? */ |
| 846 | if (p->flags == PBUF_FLAG_REF) { |
| 847 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p)); |
| 848 | /* allocate a pbuf (w/ payload) fully in RAM */ |
| 849 | /* PBUF_POOL buffers are faster if we can use them */ |
| 850 | if (p->len <= PBUF_POOL_BUFSIZE) { |
| 851 | q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); |
| 852 | if (q == NULL) { |
| 853 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n")); |
| 854 | } |
| 855 | } else { |
| 856 | /* no replacement pbuf yet */ |
| 857 | q = NULL; |
| 858 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n")); |
| 859 | } |
| 860 | /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ |
| 861 | if (q == NULL) { |
| 862 | q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); |
| 863 | if (q == NULL) { |
| 864 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM\n")); |
| 865 | } |
| 866 | } |
| 867 | /* replacement pbuf could be allocated? */ |
| 868 | if (q != NULL) |
| 869 | { |
| 870 | /* copy p to q */ |
| 871 | /* copy successor */ |
| 872 | q->next = p->next; |
| 873 | /* remove linkage from original pbuf */ |
| 874 | p->next = NULL; |
| 875 | /* remove linkage to original pbuf */ |
| 876 | if (prev != NULL) { |
| 877 | /* prev->next == p at this point */ |
| 878 | LWIP_ASSERT("prev->next == p", prev->next == p); |
| 879 | /* break chain and insert new pbuf instead */ |
| 880 | prev->next = q; |
| 881 | /* prev == NULL, so we replaced the head pbuf of the chain */ |
| 882 | } else { |
| 883 | head = q; |
| 884 | } |
| 885 | /* copy pbuf payload */ |
| 886 | memcpy(q->payload, p->payload, p->len); |
| 887 | q->tot_len = p->tot_len; |
| 888 | q->len = p->len; |
| 889 | /* in case p was the first pbuf, it is no longer refered to by |
| 890 | * our caller, as the caller MUST do p = pbuf_take(p); |
| 891 | * in case p was not the first pbuf, it is no longer refered to |
| 892 | * by prev. we can safely free the pbuf here. |
| 893 | * (note that we have set p->next to NULL already so that |
| 894 | * we will not free the rest of the chain by accident.) |
| 895 | */ |
| 896 | pbuf_free(p); |
| 897 | /* do not copy ref, since someone else might be using the old buffer */ |
| 898 | LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q)); |
| 899 | p = q; |
| 900 | } else { |
| 901 | /* deallocate chain */ |
| 902 | pbuf_free(head); |
| 903 | LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p)); |
| 904 | return NULL; |
| 905 | } |
| 906 | /* p->flags != PBUF_FLAG_REF */ |
| 907 | } else { |
| 908 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: skipping pbuf not of type PBUF_REF\n")); |
| 909 | } |
| 910 | /* remember this pbuf */ |
| 911 | prev = p; |
| 912 | /* proceed to next pbuf in original chain */ |
| 913 | p = p->next; |
| 914 | } while (p); |
| 915 | LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.\n")); |
| 916 | |
| 917 | return head; |
| 918 | } |
| 919 | |
| 920 | /** |
| 921 | * Dechains the first pbuf from its succeeding pbufs in the chain. |
| 922 | * |
| 923 | * Makes p->tot_len field equal to p->len. |
| 924 | * @param p pbuf to dechain |
| 925 | * @return remainder of the pbuf chain, or NULL if it was de-allocated. |
| 926 | * @note May not be called on a packet queue. |
| 927 | */ |
| 928 | struct pbuf * |
| 929 | pbuf_dechain(struct pbuf *p) |
| 930 | { |
| 931 | struct pbuf *q; |
| 932 | u8_t tail_gone = 1; |
| 933 | /* tail */ |
| 934 | q = p->next; |
| 935 | /* pbuf has successor in chain? */ |
| 936 | if (q != NULL) { |
| 937 | /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ |
| 938 | LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); |
| 939 | /* enforce invariant if assertion is disabled */ |
| 940 | q->tot_len = p->tot_len - p->len; |
| 941 | /* decouple pbuf from remainder */ |
| 942 | p->next = NULL; |
| 943 | /* total length of pbuf p is its own length only */ |
| 944 | p->tot_len = p->len; |
| 945 | /* q is no longer referenced by p, free it */ |
| 946 | LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); |
| 947 | tail_gone = pbuf_free(q); |
| 948 | if (tail_gone > 0) { |
| 949 | LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, |
| 950 | ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); |
| 951 | } |
| 952 | /* return remaining tail or NULL if deallocated */ |
| 953 | } |
| 954 | /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ |
| 955 | LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); |
| 956 | return (tail_gone > 0? NULL: q); |
| 957 | } |