1 #include <kernel/kernel.h>
2 #include <kernel/heap.h>
3 #include <kernel/sem.h>
4 #include <kernel/lock.h>
5 #include <kernel/smp.h>
6 #include <kernel/int.h>
7 #include <kernel/debug.h>
8 #include <kernel/cbuf.h>
10 #include <kernel/net/misc.h> // for cksum16
11 #include <kernel/arch/cpu.h>
12 #include <newos/errors.h>
17 #define VALIDATE_CBUFS 1
19 #define VALIDATE_CBUFS 0
22 #define ALLOCATE_CHUNK (PAGE_SIZE * 16)
23 #define CBUF_REGION_SIZE (1*1024*1024)
24 #define CBUF_BITMAP_SIZE (CBUF_REGION_SIZE / CBUF_LEN)
26 static cbuf
*cbuf_free_list
;
27 static mutex cbuf_free_list_lock
;
28 static cbuf
*cbuf_free_noblock_list
;
29 static spinlock_t noblock_spin
;
31 static spinlock_t cbuf_lowlevel_spinlock
;
32 static region_id cbuf_region_id
;
33 static cbuf
*cbuf_region
;
34 static region_id cbuf_bitmap_region_id
;
35 static uint8
*cbuf_bitmap
;
37 /* initialize most of the cbuf structure */
38 /* does not initialize the next pointer, because it may already be in a chain */
39 static void initialize_cbuf(cbuf
*buf
)
41 buf
->len
= sizeof(buf
->dat
);
48 static int validate_cbuf(cbuf
*head
)
58 /* make sure the first cbuf is the head */
59 if((head
->flags
& CBUF_FLAG_CHAIN_HEAD
) == 0)
60 panic("validate_cbuf: cbuf %p is not head\n", head
);
62 /* walk through the chain and verify a few things */
66 if(buf
!= head
&& (buf
->flags
& CBUF_FLAG_CHAIN_HEAD
))
67 panic("validate_cbuf: cbuf %p has buffer %p with HEAD flag set\n", head
, buf
);
69 if(buf
->next
!= NULL
&& (buf
->flags
& CBUF_FLAG_CHAIN_TAIL
))
70 panic("validate_cbuf: cbuf %p has buffer %p with TAIL flag set\n", head
, buf
);
72 /* make sure len makes sense */
73 if(buf
->len
> sizeof(buf
->dat
))
74 panic("validate_cbuf: cbuf %p has buffer %p with bad length\n", head
, buf
);
76 /* add up the size of this part of the chain */
77 counted_size
+= buf
->len
;
79 /* make sure the data pointer is inside the buffer */
80 if(((addr_t
)buf
->data
< (addr_t
)buf
->dat
)
81 || ((addr_t
)buf
->data
- (addr_t
)buf
->dat
> sizeof(buf
->dat
))
82 || ((addr_t
)buf
->data
+ buf
->len
> (addr_t
)buf
->data
+ sizeof(buf
->dat
)))
83 panic("validate_cbuf: cbuf %p has buffer %p with bad pointer\n", head
, buf
);
89 /* look at the tail */
90 if((tail
->flags
& CBUF_FLAG_CHAIN_TAIL
) == 0)
91 panic("validate_cbuf: cbuf %p 's tail at %p does not have TAIL flag set\n", head
, tail
);
93 /* make sure the added up size == the total size */
94 if(counted_size
!= head
->total_len
)
95 panic("validate_cbuf: cbuf %p has bad total_len %ld, counted %ld\n", head
, head
->total_len
, counted_size
);
100 static void *_cbuf_alloc(size_t *size
)
105 size_t len_found
= 0;
107 // dprintf("cbuf_alloc: asked to allocate size %d\n", *size);
109 int_disable_interrupts();
110 acquire_spinlock(&cbuf_lowlevel_spinlock
);
112 // scan through the allocation bitmap, looking for the first free block
115 for(i
= 0; i
< CBUF_BITMAP_SIZE
; i
++) {
116 // skip bytes of the bitmap at a time
117 if((i
% 8) == 0 && cbuf_bitmap
[i
/8] == 0xff) {
122 if(!CHECK_BIT(cbuf_bitmap
[i
/8], i
%8)) {
123 cbuf_bitmap
[i
/8] = SET_BIT(cbuf_bitmap
[i
/8], i
%8);
126 len_found
+= CBUF_LEN
;
127 if(len_found
>= *size
) {
131 } else if(start
>= 0) {
132 // we've found a start of a run before, so we're done now
138 // couldn't find any memory
142 buf
= &cbuf_region
[start
];
146 release_spinlock(&cbuf_lowlevel_spinlock
);
147 int_restore_interrupts();
152 static cbuf
*allocate_cbuf_mem(size_t size
)
156 cbuf
*last_buf
= NULL
;
157 cbuf
*head_buf
= NULL
;
162 size
= PAGE_ALIGN(size
);
166 _buf
= _cbuf_alloc(&found_size
);
168 // couldn't allocate, lets bail with what we have
172 count
= found_size
/ CBUF_LEN
;
173 // dprintf("allocate_cbuf_mem: returned %d of memory, %d left\n", found_size, size);
178 for(i
=0; i
<count
; i
++) {
179 initialize_cbuf(buf
);
181 last_buf
->next
= buf
;
182 if(buf
== head_buf
) {
183 buf
->flags
|= CBUF_FLAG_CHAIN_HEAD
;
184 buf
->total_len
= (size
/ CBUF_LEN
) * sizeof(buf
->dat
);
191 last_buf
->next
= NULL
;
192 last_buf
->flags
|= CBUF_FLAG_CHAIN_TAIL
;
198 static void _clear_chain(cbuf
*head
, cbuf
**tail
)
205 initialize_cbuf(buf
); // doesn't touch the next ptr
213 void cbuf_free_chain_noblock(cbuf
*buf
)
221 _clear_chain(head
, &last
);
223 int_disable_interrupts();
224 acquire_spinlock(&noblock_spin
);
226 last
->next
= cbuf_free_noblock_list
;
227 cbuf_free_noblock_list
= head
;
229 release_spinlock(&noblock_spin
);
230 int_restore_interrupts();
233 void cbuf_free_chain(cbuf
*buf
)
241 _clear_chain(head
, &last
);
243 mutex_lock(&cbuf_free_list_lock
);
245 last
->next
= cbuf_free_list
;
246 cbuf_free_list
= head
;
248 mutex_unlock(&cbuf_free_list_lock
);
251 cbuf
*cbuf_get_chain(size_t len
)
256 size_t chain_len
= 0;
259 panic("cbuf_get_chain: passed size 0\n");
261 mutex_lock(&cbuf_free_list_lock
);
263 while(chain_len
< len
) {
264 if(cbuf_free_list
== NULL
) {
265 // we need to allocate some more cbufs
266 mutex_unlock(&cbuf_free_list_lock
);
267 temp
= allocate_cbuf_mem(ALLOCATE_CHUNK
);
271 cbuf_free_chain(chain
);
272 dprintf("cbuf_get_chain: asked to allocate %ld bytes but out of memory\n", len
);
275 cbuf_free_chain(temp
);
276 mutex_lock(&cbuf_free_list_lock
);
280 temp
= cbuf_free_list
;
281 cbuf_free_list
= cbuf_free_list
->next
;
288 chain_len
+= chain
->len
;
290 mutex_unlock(&cbuf_free_list_lock
);
292 // now we have a chain, fixup the first and last entry
293 chain
->total_len
= len
;
294 chain
->flags
|= CBUF_FLAG_CHAIN_HEAD
;
295 tail
->len
-= chain_len
- len
;
296 tail
->flags
|= CBUF_FLAG_CHAIN_TAIL
;
298 validate_cbuf(chain
);
303 cbuf
*cbuf_get_chain_noblock(size_t len
)
308 size_t chain_len
= 0;
310 int_disable_interrupts();
311 acquire_spinlock(&noblock_spin
);
313 while(chain_len
< len
) {
314 if(cbuf_free_noblock_list
== NULL
) {
315 dprintf("cbuf_get_chain_noblock: not enough cbufs\n");
316 release_spinlock(&noblock_spin
);
317 int_restore_interrupts();
320 cbuf_free_chain_noblock(chain
);
325 temp
= cbuf_free_noblock_list
;
326 cbuf_free_noblock_list
= cbuf_free_noblock_list
->next
;
332 chain_len
+= chain
->len
;
334 release_spinlock(&noblock_spin
);
335 int_restore_interrupts();
337 // now we have a chain, fixup the first and last entry
338 chain
->total_len
= len
;
339 chain
->flags
|= CBUF_FLAG_CHAIN_HEAD
;
340 tail
->len
-= chain_len
- len
;
341 tail
->flags
|= CBUF_FLAG_CHAIN_TAIL
;
346 int cbuf_memcpy_to_chain(cbuf
*chain
, size_t offset
, const void *_src
, size_t len
)
349 char *src
= (char *)_src
;
352 validate_cbuf(chain
);
354 if(offset
+ len
> chain
->total_len
) {
355 panic("cbuf_memcpy_to_chain: offset + len > size of cbuf chain\n");
356 return ERR_INVALID_ARGS
;
359 // find the starting cbuf in the chain to copy to
364 panic("cbuf_memcpy_to_chain: end of chain reached too early!\n");
367 if(offset
< buf
->len
) {
380 panic("cbuf_memcpy_to_chain: end of chain reached too early!\n");
383 to_copy
= min(len
, buf
->len
- buf_offset
);
384 memcpy((char *)buf
->data
+ buf_offset
, src
, to_copy
);
395 int cbuf_user_memcpy_to_chain(cbuf
*chain
, size_t offset
, const void *_src
, size_t len
)
398 char *src
= (char *)_src
;
402 validate_cbuf(chain
);
404 if(len
+ offset
> chain
->total_len
) {
405 dprintf("cbuf_memcpy_to_chain: len + offset > size of cbuf chain\n");
406 return ERR_INVALID_ARGS
;
409 // find the starting cbuf in the chain to copy to
414 dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
417 if(offset
< buf
->len
) {
431 dprintf("cbuf_memcpy_to_chain: end of chain reached too early!\n");
434 to_copy
= min(len
, buf
->len
- buf_offset
);
435 if ((err
= user_memcpy((char *)buf
->data
+ buf_offset
, src
, to_copy
) < 0))
436 break; // memory exception
448 int cbuf_memcpy_from_chain(void *_dest
, cbuf
*chain
, size_t offset
, size_t len
)
451 char *dest
= (char *)_dest
;
454 validate_cbuf(chain
);
456 if(len
+ offset
> chain
->total_len
) {
457 dprintf("cbuf_memcpy_from_chain: len + offset > size of cbuf chain\n");
458 return ERR_INVALID_ARGS
;
461 // find the starting cbuf in the chain to copy from
466 dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
469 if(offset
< buf
->len
) {
482 dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
486 to_copy
= min(len
, buf
->len
- buf_offset
);
487 memcpy(dest
, (char *)buf
->data
+ buf_offset
, to_copy
);
498 int cbuf_user_memcpy_from_chain(void *_dest
, cbuf
*chain
, size_t offset
, size_t len
)
501 char *dest
= (char *)_dest
;
505 validate_cbuf(chain
);
507 if(len
+ offset
> chain
->total_len
) {
508 dprintf("cbuf_memcpy_from_chain: len + offset > size of cbuf chain\n");
509 return ERR_INVALID_ARGS
;
512 // find the starting cbuf in the chain to copy from
517 dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
520 if(offset
< buf
->len
) {
534 dprintf("cbuf_memcpy_from_chain: end of chain reached too early!\n");
538 to_copy
= min(len
, buf
->len
- buf_offset
);
539 if ((err
= user_memcpy(dest
, (char *)buf
->data
+ buf_offset
, to_copy
) < 0))
551 cbuf
*cbuf_duplicate_chain(cbuf
*chain
, size_t offset
, size_t len
, size_t leading_space
)
562 validate_cbuf(chain
);
564 if(offset
>= chain
->total_len
)
566 len
= min(len
, chain
->total_len
- offset
);
568 newbuf
= cbuf_get_chain(len
+ leading_space
);
572 if(leading_space
> 0) {
573 cbuf_truncate_head(newbuf
, leading_space
, false);
576 // find the starting cbuf in the chain to copy from
581 cbuf_free_chain(newbuf
);
582 dprintf("cbuf_duplicate_chain: end of chain reached too early!\n");
585 if(offset
< buf
->len
) {
600 cbuf_free_chain(newbuf
);
601 dprintf("cbuf_duplicate_chain: end of source chain reached too early!\n");
604 if(destbuf
== NULL
) {
605 cbuf_free_chain(newbuf
);
606 dprintf("cbuf_duplicate_chain: end of destination chain reached too early!\n");
610 to_copy
= min(destbuf
->len
- dest_buf_offset
, buf
->len
- buf_offset
);
611 to_copy
= min(to_copy
, len
);
612 memcpy((char *)destbuf
->data
+ dest_buf_offset
, (char *)buf
->data
+ buf_offset
, to_copy
);
615 if(to_copy
+ buf_offset
== buf
->len
) {
619 buf_offset
+= to_copy
;
621 if(to_copy
+ dest_buf_offset
== destbuf
->len
) {
622 destbuf
= destbuf
->next
;
625 dest_buf_offset
+= to_copy
;
629 validate_cbuf(newbuf
);
635 cbuf
*cbuf_merge_chains(cbuf
*chain1
, cbuf
*chain2
)
639 if(!chain1
&& !chain2
)
648 validate_cbuf(chain1
);
649 validate_cbuf(chain2
);
651 // walk to the end of the first chain and tag the second one on
658 // modify the flags on the chain headers
659 buf
->flags
&= ~CBUF_FLAG_CHAIN_TAIL
;
660 chain1
->total_len
+= chain2
->total_len
;
661 chain2
->flags
&= ~CBUF_FLAG_CHAIN_HEAD
;
666 size_t cbuf_get_len(cbuf
*buf
)
673 if(buf
->flags
& CBUF_FLAG_CHAIN_HEAD
) {
674 return buf
->total_len
;
685 void *cbuf_get_ptr(cbuf
*buf
, size_t offset
)
690 if(buf
->len
> offset
)
691 return (void *)((int)buf
->data
+ offset
);
692 if(buf
->len
> offset
)
700 int cbuf_is_contig_region(cbuf
*buf
, size_t start
, size_t end
)
705 if(buf
->len
> start
) {
706 if(buf
->len
- start
>= end
)
718 static uint16
_cbuf_ones_cksum16(cbuf
*buf
, size_t offset
, size_t len
, uint16 sum
)
727 // find the start ptr
729 if(buf
->len
> offset
)
731 if(buf
->len
> offset
)
737 // start checksumming
738 while(buf
&& len
> 0) {
739 void *ptr
= (void *)((addr_t
)buf
->data
+ offset
);
740 size_t plen
= min(len
, buf
->len
- offset
);
742 sum
= ones_sum16(sum
, ptr
, plen
);
747 // if the pointer was odd, or the length was odd, but not both,
748 // the checksum was swapped
749 if((buf
&& len
> 0) && (((offset
% 2) && ((plen
% 2) == 0)) || (((offset
% 2) == 0) && (plen
% 2)))) {
751 sum
= ((sum
& 0xff) << 8) | ((sum
>> 8) & 0xff);
757 sum
= ((sum
& 0xff) << 8) | ((sum
>> 8) & 0xff);
762 uint16
cbuf_ones_cksum16(cbuf
*chain
, size_t offset
, size_t len
)
764 return ~_cbuf_ones_cksum16(chain
, offset
, len
, 0);
767 uint16
cbuf_ones_cksum16_2(cbuf
*chain
, size_t offset
, size_t len
, void *buf
, size_t buf_len
)
769 uint16 sum
= ones_sum16(0, buf
, buf_len
);
770 return ~_cbuf_ones_cksum16(chain
, offset
, len
, sum
);
773 cbuf
*cbuf_truncate_head(cbuf
*buf
, size_t trunc_bytes
, bool free_unused
)
776 cbuf
*free_chain
= NULL
;
783 while(buf
&& trunc_bytes
> 0) {
786 to_trunc
= min(trunc_bytes
, buf
->len
);
788 buf
->len
-= to_trunc
;
789 buf
->data
= (void *)((int)buf
->data
+ to_trunc
);
791 trunc_bytes
-= to_trunc
;
792 head
->total_len
-= to_trunc
;
795 if(free_unused
&& buf
&& head
->len
== 0) {
796 // the head cbuf is now zero length
797 buf
->total_len
= head
->total_len
;
798 buf
->flags
|= CBUF_FLAG_CHAIN_HEAD
;
799 buf
->packet_next
= head
->packet_next
;
801 head
->next
= free_chain
;
809 cbuf_free_chain(free_chain
);
816 int cbuf_truncate_tail(cbuf
*buf
, size_t trunc_bytes
, bool free_unused
)
821 cbuf
*free_chain
= NULL
;
825 if(trunc_bytes
> buf
->total_len
)
826 trunc_bytes
= buf
->total_len
;
828 offset
= buf
->total_len
- trunc_bytes
;
830 while(buf
&& offset
> 0) {
831 if(offset
< buf
->len
) {
842 head
->total_len
-= buf
->len
- buf_offset
;
843 buf
->len
-= buf
->len
- buf_offset
;
845 // clear out the rest of the buffers in this chain
850 // account for the loss of this buffer
851 head
->total_len
-= buf
->len
;
855 // stick it on the free list that we'll dispose of in a bit
856 temp
->next
= free_chain
;
862 cbuf_free_chain(free_chain
);
869 int cbuf_extend_head(cbuf
**_buf
, size_t extend_bytes
)
873 if(!_buf
|| !(*_buf
))
874 return ERR_INVALID_ARGS
;
880 // first, see how much space we can allocate off the front of the chain
881 if(buf
->len
< sizeof(buf
->dat
) && (addr_t
)buf
->data
!= (addr_t
)buf
->dat
) {
882 // there is some space at the front of this buffer, lets see how much
886 // check to make sure the data pointer is inside the dat buffer in this cbuf
887 ASSERT((addr_t
)buf
->data
> (addr_t
)buf
->dat
);
888 ASSERT((addr_t
)buf
->data
- (addr_t
)buf
->dat
< sizeof(buf
->dat
));
890 available
= (addr_t
)buf
->data
- (addr_t
)buf
->dat
;
891 to_extend
= min(available
, extend_bytes
);
893 buf
->len
+= to_extend
;
894 buf
->data
= (void *)((addr_t
)buf
->data
- to_extend
);
895 extend_bytes
-= to_extend
;
898 if(extend_bytes
> 0) {
901 new_buf
= cbuf_get_chain(extend_bytes
);
903 return ERR_NO_MEMORY
;
905 if(new_buf
->next
== NULL
) {
906 // simple case, the head extension is a single buffer.
907 // move the data in the head to the end of the cbuf (so subsequent extends)
908 // have a better shot at being able to reuse the cbuf.
911 ASSERT(new_buf
->len
<= sizeof(new_buf
->dat
));
913 move_size
= sizeof(new_buf
->dat
) - new_buf
->len
;
915 buf
->data
= (void *)((addr_t
)buf
->data
+ move_size
);
918 buf
= cbuf_merge_chains(new_buf
, buf
);
926 int cbuf_extend_tail(cbuf
*head
, size_t extend_bytes
)
932 return ERR_INVALID_ARGS
;
936 // walk to the end of this buffer
937 for(temp
= head
; temp
->next
!= NULL
; temp
= temp
->next
)
940 return ERR_INVALID_ARGS
;
942 // calculate the available space in this cbuf
943 ASSERT((addr_t
)temp
->data
>= (addr_t
)temp
->dat
);
944 ASSERT((addr_t
)temp
->data
- (addr_t
)temp
->dat
<= sizeof(temp
->dat
));
945 ASSERT((addr_t
)temp
->data
+ temp
->len
<= (addr_t
)temp
->dat
+ sizeof(temp
->dat
));
947 available
= sizeof(temp
->dat
) - (temp
->len
+ ((addr_t
)temp
->data
- (addr_t
)temp
->dat
));
949 // we can extend by adding
950 size_t extend_by
= min(available
, extend_bytes
);
952 temp
->len
+= extend_by
;
953 head
->total_len
+= extend_by
;
954 extend_bytes
-= extend_by
;
957 if(extend_bytes
> 0) {
958 // we still need to extend
961 new_buf
= cbuf_get_chain(extend_bytes
);
963 // XXX undo any previons extension we may have done
964 return ERR_NO_MEMORY
;
967 cbuf_merge_chains(head
, new_buf
);
974 static void dbg_dump_cbuf_freelists(int argc
, char **argv
)
978 dprintf("cbuf_free_list:\n");
979 for(buf
= cbuf_free_list
; buf
; buf
= buf
->next
)
983 dprintf("cbuf_free_noblock_list:\n");
984 for(buf
= cbuf_free_noblock_list
; buf
; buf
= buf
->next
)
995 dprintf("starting cbuffer test\n");
997 buf
= cbuf_get_chain(32);
999 panic("cbuf_test: failed allocation of 32\n");
1001 buf2
= cbuf_get_chain(3*1024*1024);
1003 panic("cbuf_test: failed allocation of 3mb\n");
1005 buf
= cbuf_merge_chains(buf2
, buf
);
1007 cbuf_free_chain(buf
);
1009 dprintf("allocating too much...\n");
1011 buf
= cbuf_get_chain(128*1024*1024);
1013 panic("cbuf_test: should have failed to allocate 128mb\n");
1015 dprintf("touching memory allocated by cbuf\n");
1017 buf
= cbuf_get_chain(7*1024*1024);
1019 panic("cbuf_test: failed allocation of 7mb\n");
1021 for(i
=0; i
< sizeof(temp
); i
++)
1023 for(i
=0; i
<7*1024*1024 / sizeof(temp
); i
++) {
1024 if(i
% 128 == 0) dprintf("%Lud\n", (long long)(i
*sizeof(temp
)));
1025 cbuf_memcpy_to_chain(buf
, i
*sizeof(temp
), temp
, sizeof(temp
));
1027 cbuf_free_chain(buf
);
1029 dprintf("finished cbuffer test\n");
1038 cbuf_free_list
= NULL
;
1039 cbuf_free_noblock_list
= NULL
;
1041 cbuf_lowlevel_spinlock
= 0;
1043 // add the debug command
1044 dbg_add_command(&dbg_dump_cbuf_freelists
, "cbuf_freelist", "Dumps the cbuf free lists");
1046 err
= mutex_init(&cbuf_free_list_lock
, "cbuf_free_list_lock");
1048 panic("cbuf_init: error creating cbuf_free_list_lock\n");
1049 return ERR_NO_MEMORY
;
1052 cbuf_region_id
= vm_create_anonymous_region(vm_get_kernel_aspace_id(), "cbuf region",
1053 (void **)&cbuf_region
, REGION_ADDR_ANY_ADDRESS
, CBUF_REGION_SIZE
, REGION_WIRING_LAZY
, LOCK_RW
|LOCK_KERNEL
);
1054 if(cbuf_region_id
< 0) {
1055 panic("cbuf_init: error creating cbuf region\n");
1056 return ERR_NO_MEMORY
;
1059 cbuf_bitmap_region_id
= vm_create_anonymous_region(vm_get_kernel_aspace_id(), "cbuf bitmap region",
1060 (void **)&cbuf_bitmap
, REGION_ADDR_ANY_ADDRESS
,
1061 CBUF_BITMAP_SIZE
/ 8, REGION_WIRING_WIRED
, LOCK_RW
|LOCK_KERNEL
);
1062 if(cbuf_region_id
< 0) {
1063 panic("cbuf_init: error creating cbuf bitmap region\n");
1064 return ERR_NO_MEMORY
;
1067 // initialize the bitmap
1068 for(i
=0; i
<CBUF_BITMAP_SIZE
/8; i
++)
1072 buf
= allocate_cbuf_mem(ALLOCATE_CHUNK
);
1074 return ERR_NO_MEMORY
;
1075 cbuf_free_chain_noblock(buf
);
1077 buf
= allocate_cbuf_mem(ALLOCATE_CHUNK
);
1079 return ERR_NO_MEMORY
;
1081 cbuf_free_chain(buf
);