1 |
#include "lwip/opt.h" |
2 |
#include "lwip/pbuf.h" |
3 |
#include "lwip/sys.h" |
4 |
#include "arch/perf.h" |
5 |
|
6 |
void |
7 |
pbuf_init(void){ |
8 |
return; |
9 |
} |
10 |
|
11 |
/* |
12 |
* abstracted pbuf interface functions |
13 |
* these can be replaced with #defines, but for the time being |
14 |
* we use functions so that we can check for stupidity |
15 |
*/ |
16 |
struct pbuf * |
17 |
pbuf_alloc_new(u16_t offset, u16_t size, struct pbuf_manager *mgr, void *src){ |
18 |
LWIP_ASSERT("pbuf_alloc(): NULL pbuf manager", mgr!=NULL); |
19 |
return mgr->pbuf_alloc_new(offset, size, mgr, src); |
20 |
} |
21 |
|
22 |
void |
23 |
pbuf_realloc(struct pbuf *p, u16_t new_len){ |
24 |
LWIP_ASSERT("pbuf_realloc(): NULL pbuf", p!=NULL); |
25 |
LWIP_ASSERT("pbuf_realloc(): NULL pbuf manager", p->manager!=NULL); |
26 |
struct pbuf *q; |
27 |
u16_t rem_len; /* remaining length */ |
28 |
s16_t grow; |
29 |
|
30 |
/* desired length larger than current length? */ |
31 |
if (new_len >= p->tot_len) { |
32 |
/* enlarging not yet supported */ |
33 |
return; |
34 |
} |
35 |
|
36 |
/* the pbuf chain grows by (new_len - p->tot_len) bytes |
37 |
* (which may be negative in case of shrinking) */ |
38 |
grow = new_len - p->tot_len; |
39 |
|
40 |
/* first, step over any pbufs that should remain in the chain */ |
41 |
rem_len = new_len; |
42 |
q = p; |
43 |
/* should this pbuf be kept? */ |
44 |
while (rem_len > q->len) { |
45 |
/* decrease remaining length by pbuf length */ |
46 |
rem_len -= q->len; |
47 |
/* decrease total length indicator */ |
48 |
q->tot_len += grow; |
49 |
/* proceed to next pbuf in chain */ |
50 |
q = q->next; |
51 |
} |
52 |
/* we have now reached the new last pbuf (in q) */ |
53 |
/* rem_len == desired length for pbuf q */ |
54 |
|
55 |
/* shrink allocated memory */ |
56 |
/* (may only adjust their length fields) */ |
57 |
|
58 |
/* reallocate and adjust the length of the pbuf that will be split */ |
59 |
p->manager->pbuf_realloc(q, rem_len); |
60 |
|
61 |
/* adjust length fields for new last pbuf */ |
62 |
q->len = rem_len; |
63 |
q->tot_len = q->len; |
64 |
|
65 |
/* any remaining pbufs in chain? */ |
66 |
if (q->next != NULL) { |
67 |
/* free remaining pbufs in chain */ |
68 |
pbuf_free(q->next); |
69 |
} |
70 |
/* q is last packet in chain */ |
71 |
q->next = NULL; |
72 |
} |
73 |
|
74 |
u8_t |
75 |
pbuf_header(struct pbuf *p, s16_t header_size){ |
76 |
LWIP_ASSERT("pbuf_header(): NULL pbuf", p!=NULL); |
77 |
LWIP_ASSERT("pbuf_header(): NULL pbuf manager", p->manager!=NULL); |
78 |
return p->manager->pbuf_header(p, header_size); |
79 |
} |
80 |
|
81 |
u8_t |
82 |
pbuf_free(struct pbuf *p){ |
83 |
struct pbuf *q; |
84 |
u8_t count; |
85 |
|
86 |
if (p == NULL) { |
87 |
LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, |
88 |
("pbuf_free(p == NULL) was called.\n")); |
89 |
return 0; |
90 |
} |
91 |
|
92 |
LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_free(%p)\n", (void *)p)); |
93 |
|
94 |
PERF_START; |
95 |
|
96 |
count = 0; |
97 |
|
98 |
/* Since decrementing ref cannot be guaranteed to be a single machine |
99 |
* operation we must protect it. Also, the later test of ref must be |
100 |
* protected. |
101 |
*/ |
102 |
SYS_ARCH_PROTECT(old_level); |
103 |
|
104 |
/* de-allocate all consecutive pbufs from the head of the chain that |
105 |
* obtain a zero reference count after decrementing |
106 |
*/ |
107 |
while (p != NULL) { |
108 |
/* all pbufs in a chain are referenced at least once */ |
109 |
LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); |
110 |
|
111 |
/* decrease reference count (number of pointers to pbuf) */ |
112 |
p->ref--; |
113 |
|
114 |
/* this pbuf is no longer referenced to? */ |
115 |
if (p->ref == 0) { |
116 |
/* remember next pbuf in chain for next iteration */ |
117 |
q = p->next; |
118 |
LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p)); |
119 |
|
120 |
/* call the pbuf-specific free function */ |
121 |
p->manager->pbuf_free(p); |
122 |
|
123 |
count++; |
124 |
|
125 |
/* proceed to next pbuf */ |
126 |
p = q; |
127 |
|
128 |
} else { |
129 |
/* p->ref > 0, this pbuf is still referenced to */ |
130 |
/* (and so the remaining pbufs in chain as well) */ |
131 |
|
132 |
LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_free: %p has ref %u, ending here.\n", |
133 |
(void *)p, (unsigned int)p->ref)); |
134 |
|
135 |
/* stop walking through chain */ |
136 |
p = NULL; |
137 |
} |
138 |
} |
139 |
|
140 |
SYS_ARCH_UNPROTECT(old_level); |
141 |
|
142 |
PERF_STOP("pbuf_free"); |
143 |
|
144 |
/* return number of de-allocated pbufs */ |
145 |
return count; |
146 |
} |
147 |
|
148 |
|
149 |
/* |
150 |
* generic pbuf interface functions |
151 |
*/ |
152 |
|
153 |
void |
154 |
pbuf_ref_chain(struct pbuf *p){ |
155 |
LWIP_ASSERT("pbuf_ref_chain unimplemented!", 0); |
156 |
} |
157 |
|
158 |
void |
159 |
pbuf_ref(struct pbuf *p){ |
160 |
SYS_ARCH_DECL_PROTECT(old_level); |
161 |
/* pbuf given? */ |
162 |
if (p != NULL) { |
163 |
SYS_ARCH_PROTECT(old_level); |
164 |
++(p->ref); |
165 |
SYS_ARCH_UNPROTECT(old_level); |
166 |
} |
167 |
} |
168 |
|
169 |
u8_t |
170 |
pbuf_clen(struct pbuf *p){ |
171 |
u8_t len; |
172 |
|
173 |
len = 0; |
174 |
while (p != NULL) { |
175 |
++len; |
176 |
#if 0 |
177 |
{ |
178 |
mem_ptr_t x = (mem_ptr_t)p; |
179 |
if((x%sizeof(mem_ptr_t))!=0){ |
180 |
LWIP_ASSERT("pbuf not aligned!\n", 0); |
181 |
} |
182 |
} |
183 |
#endif |
184 |
p = p->next; |
185 |
} |
186 |
|
187 |
return len; |
188 |
} |
189 |
|
190 |
void |
191 |
pbuf_cat(struct pbuf *h, struct pbuf *t){ |
192 |
struct pbuf *p; |
193 |
|
194 |
LWIP_ASSERT("h != NULL", h != NULL); |
195 |
LWIP_ASSERT("t != NULL", t != NULL); |
196 |
|
197 |
if (t == NULL) |
198 |
return; |
199 |
|
200 |
/* proceed to last pbuf of chain */ |
201 |
for (p = h; p->next != NULL; p = p->next) { |
202 |
/* add total length of second chain to all totals of first chain */ |
203 |
p->tot_len += t->tot_len; |
204 |
} |
205 |
|
206 |
/* p is last pbuf of first h chain */ |
207 |
LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", |
208 |
p->tot_len == p->len); |
209 |
|
210 |
/* add total length of second chain to last pbuf total of first chain */ |
211 |
p->tot_len += t->tot_len; |
212 |
|
213 |
/* chain last pbuf of head (p) with first of tail (t) */ |
214 |
p->next = t; |
215 |
} |
216 |
|
217 |
void |
218 |
pbuf_chain(struct pbuf *h, struct pbuf *t){ |
219 |
pbuf_cat(h, t); |
220 |
/* t is now referenced to one more time */ |
221 |
pbuf_ref(t); |
222 |
LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, |
223 |
("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); |
224 |
} |
225 |
|
226 |
struct pbuf * |
227 |
pbuf_take(struct pbuf *f){ |
228 |
LWIP_ASSERT("pbuf_take() unimplemented", 0); |
229 |
return NULL; |
230 |
} |
231 |
|
232 |
struct pbuf * |
233 |
pbuf_dechain(struct pbuf *p){ |
234 |
struct pbuf *q; |
235 |
u8_t tail_gone = 1; |
236 |
/* tail */ |
237 |
q = p->next; |
238 |
/* pbuf has successor in chain? */ |
239 |
if (q != NULL) { |
240 |
/* assert tot_len invariant: |
241 |
* (p->tot_len == p->len + (p->next? p->next->tot_len : 0) |
242 |
*/ |
243 |
LWIP_ASSERT("p->tot_len == p->len + q->tot_len", |
244 |
q->tot_len == p->tot_len - p->len); |
245 |
|
246 |
/* enforce invariant if assertion is disabled */ |
247 |
q->tot_len = p->tot_len - p->len; |
248 |
|
249 |
/* decouple pbuf from remainder */ |
250 |
p->next = NULL; |
251 |
|
252 |
/* total length of pbuf p is its own length only */ |
253 |
p->tot_len = p->len; |
254 |
|
255 |
/* q is no longer referenced by p, free it */ |
256 |
LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, |
257 |
("pbuf_dechain: unreferencing %p\n", (void *)q)); |
258 |
|
259 |
tail_gone = pbuf_free(q); |
260 |
if (tail_gone > 0) |
261 |
LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, |
262 |
("pbuf_dechain: deallocated %p (no longer referenced)\n", |
263 |
(void *)q)); |
264 |
} |
265 |
|
266 |
/* assert tot_len invariant: |
267 |
* (p->tot_len == p->len + (p->next? p->next->tot_len: 0) |
268 |
*/ |
269 |
LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); |
270 |
|
271 |
return (tail_gone > 0? NULL: q); |
272 |
} |