mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
iovec: initial implementation
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#define _ABI_H
|
||||
|
||||
#include <time.h>
|
||||
#include <kernel/iovec.h>
|
||||
|
||||
typedef int pid_t;
|
||||
typedef int tid_t;
|
||||
@@ -115,20 +116,6 @@ typedef struct {
|
||||
char sun_path[108];
|
||||
} __attribute__((packed)) unaddr_t;
|
||||
|
||||
typedef struct {
|
||||
void *addr;
|
||||
size_t len;
|
||||
} iovec_t;
|
||||
|
||||
static inline size_t iovec_size(iovec_t *iovec, size_t count) {
|
||||
size_t size = 0;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
size += iovec[i].len;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void *addr;
|
||||
size_t addrlen;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef _IOVEC_H
|
||||
#define _IOVEC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ringbuffer.h>
|
||||
|
||||
typedef struct {
|
||||
void *addr;
|
||||
size_t len;
|
||||
} iovec_t;
|
||||
|
||||
typedef struct {
|
||||
iovec_t *iovec;
|
||||
size_t count;
|
||||
|
||||
iovec_t *current;
|
||||
uintmax_t current_offset;
|
||||
|
||||
uintmax_t total_offset;
|
||||
size_t total_size;
|
||||
} iovec_iterator_t;
|
||||
|
||||
// checks if all iovec[x].addr is an userspace address
|
||||
bool iovec_user_check(iovec_t *iovec, size_t count);
|
||||
|
||||
// returns the sum of the size of all iovecs
|
||||
size_t iovec_size(iovec_t *iovec, size_t count);
|
||||
|
||||
// initializes an iovec iterator
|
||||
void iovec_iterator_init(iovec_iterator_t *iovec_iterator, iovec_t *iovec, size_t count);
|
||||
|
||||
// skips skip_count bytes in the iovec iterator, returns the remaining number of bytes
|
||||
size_t iovec_iterator_skip(iovec_iterator_t *iovec_iterator, size_t skip_count);
|
||||
|
||||
// sets the current offset of the iovec iterator to offset, returns the remaining number of bytes
|
||||
size_t iovec_iterator_set(iovec_iterator_t *iovec_iterator, size_t offset);
|
||||
|
||||
// copies byte_count bytes from the iovec_iterator into buffer
|
||||
// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with EFAULT
|
||||
int iovec_iterator_copy_to_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count);
|
||||
|
||||
// copies byte_count bytes from buffer into the iovec_iterator
|
||||
// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with EFAULT
|
||||
int iovec_iterator_copy_from_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count);
|
||||
|
||||
// copies byte_count bytes from the iovec_iterator into the ringbuffer
|
||||
// returns the number of bytes copied
|
||||
// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail by returning RINGBUFFER_USER_COPY_FAILED
|
||||
size_t iovec_iterator_copy_to_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count);
|
||||
|
||||
// copies byte_count bytes from the ringbuffer into the iovec_iterator
|
||||
// returns the number of bytes copied
|
||||
// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with RINGBUFFER_USER_COPY_FAILED
|
||||
size_t iovec_iterator_copy_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count);
|
||||
|
||||
// copies byte_count bytes from the ringbuffer into the iovec_iterator, without moving the ringbuffer read pointer
|
||||
// returns the number of bytes copied
|
||||
// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with RINGBUFFER_USER_COPY_FAILED
|
||||
size_t iovec_iterator_peek_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count, uintmax_t ringbuffer_offset);
|
||||
|
||||
// returns the physical address of the next page in the iovec_iterator, adding a reference count while at it
|
||||
// fails with EINVAL if the iterator does not currently point to the start of a page or there is not enough space for a page
|
||||
// fails with EFAULT if the page is not mapped
|
||||
int iovec_iterator_next_page(iovec_iterator_t *iovec_iterator, void **page);
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <mutex.h>
|
||||
#include <kernel/poll.h>
|
||||
#include <kernel/usercopy.h>
|
||||
#include <kernel/iovec.h>
|
||||
|
||||
#define SOCKET_STATE_UNBOUND 0
|
||||
#define SOCKET_STATE_BOUND 1
|
||||
|
||||
@@ -74,12 +74,14 @@ static void bytestolba(blockdesc_t *desc, uintmax_t offset, size_t size, uintmax
|
||||
|
||||
#define MAP_FLAGS (ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC)
|
||||
|
||||
// this is only really called by the page cache VOP_GETPAGE and VOP_PUTPAGE functions, so we can make a few assumptions
|
||||
static int rwblock(int minor, void *buffer, size_t size, uintmax_t offset, int flags, bool write, size_t *done) {
|
||||
blockdesc_t *desc = getdesc(minor);
|
||||
if (desc == NULL)
|
||||
return ENODEV;
|
||||
|
||||
__assert((size % desc->blocksize) == 0);
|
||||
__assert((size % PAGE_SIZE) == 0);
|
||||
uintmax_t bytetop = desc->blockcapacity * desc->blocksize;
|
||||
|
||||
// offset past end
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#include <kernel/iovec.h>
|
||||
#include <kernel/vmm.h>
|
||||
#include <kernel/usercopy.h>
|
||||
|
||||
static inline bool iovec_iterator_finished(iovec_iterator_t *iovec_iterator) {
|
||||
return iovec_iterator->total_size == iovec_iterator->total_offset;
|
||||
}
|
||||
|
||||
bool iovec_user_check(iovec_t *iovec, size_t count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (IS_USER_ADDRESS(iovec[i].addr) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t iovec_size(iovec_t *iovec, size_t count) {
|
||||
size_t size = 0;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
size += iovec[i].len;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void iovec_iterator_init(iovec_iterator_t *iovec_iterator, iovec_t *iovec, size_t count) {
|
||||
iovec_iterator->iovec = iovec;
|
||||
iovec_iterator->count = count;
|
||||
|
||||
iovec_iterator->current = iovec;
|
||||
iovec_iterator->current_offset = 0;
|
||||
|
||||
iovec_iterator->total_offset = 0;
|
||||
iovec_iterator->total_size = iovec_size(iovec, count);
|
||||
}
|
||||
|
||||
size_t iovec_iterator_skip(iovec_iterator_t *iovec_iterator, size_t skip_count) {
|
||||
for (;;) {
|
||||
size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
size_t skip_current = min(remaining_current, skip_count);
|
||||
|
||||
skip_count -= skip_current;
|
||||
|
||||
iovec_iterator->total_offset += skip_current;
|
||||
iovec_iterator->current_offset += skip_current;
|
||||
|
||||
// end of current iovec, go to next
|
||||
if (iovec_iterator->current_offset == iovec_iterator->current->len && iovec_iterator_finished(iovec_iterator) == false) {
|
||||
iovec_iterator->current++;
|
||||
iovec_iterator->current_offset = 0;
|
||||
}
|
||||
|
||||
// end of skip or end of iovecs
|
||||
if (skip_count == 0 || iovec_iterator_finished(iovec_iterator))
|
||||
break;
|
||||
}
|
||||
|
||||
return iovec_iterator->total_size - iovec_iterator->total_offset;
|
||||
}
|
||||
|
||||
size_t iovec_iterator_set(iovec_iterator_t *iovec_iterator, size_t offset) {
|
||||
iovec_iterator->current = iovec_iterator->current;
|
||||
iovec_iterator->current_offset = 0;
|
||||
iovec_iterator->total_offset = 0;
|
||||
|
||||
return iovec_iterator_skip(iovec_iterator, offset);
|
||||
}
|
||||
|
||||
int iovec_iterator_copy_to_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count) {
|
||||
size_t iterator_offset_save = iovec_iterator->total_offset;
|
||||
size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset);
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
size_t copy_current = min(remaining_current, remaining_total);
|
||||
|
||||
error = USERCOPY_POSSIBLY_FROM_USER(buffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), copy_current);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
iovec_iterator_skip(iovec_iterator, copy_current);
|
||||
remaining_total -= copy_current;
|
||||
|
||||
if (iovec_iterator_finished(iovec_iterator) || remaining_total == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (error)
|
||||
iovec_iterator_set(iovec_iterator, iterator_offset_save);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int iovec_iterator_copy_from_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count) {
|
||||
size_t iterator_offset_save = iovec_iterator->total_offset;
|
||||
size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset);
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
size_t copy_current = min(remaining_current, remaining_total);
|
||||
|
||||
error = USERCOPY_POSSIBLY_FROM_USER((void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), buffer, copy_current);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
iovec_iterator_skip(iovec_iterator, copy_current);
|
||||
remaining_total -= copy_current;
|
||||
|
||||
if (iovec_iterator_finished(iovec_iterator) || remaining_total == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (error)
|
||||
iovec_iterator_set(iovec_iterator, iterator_offset_save);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
size_t iovec_iterator_copy_to_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count) {
|
||||
size_t iterator_offset_save = iovec_iterator->total_offset;
|
||||
size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset);
|
||||
size_t total_done = 0;
|
||||
size_t current_done = 0;
|
||||
|
||||
for (;;) {
|
||||
size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
size_t copy_current = min(remaining_current, remaining_total);
|
||||
|
||||
current_done = ringbuffer_write(ringbuffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), copy_current);
|
||||
if (current_done == RINGBUFFER_USER_COPY_FAILED) {
|
||||
// remove the data that was inserted
|
||||
ringbuffer_remove(ringbuffer, total_done);
|
||||
break;
|
||||
}
|
||||
|
||||
if (current_done == 0)
|
||||
break;
|
||||
|
||||
iovec_iterator_skip(iovec_iterator, copy_current);
|
||||
total_done += current_done;
|
||||
|
||||
if (total_done == byte_count || iovec_iterator_finished(iovec_iterator))
|
||||
break;
|
||||
}
|
||||
|
||||
if (current_done == RINGBUFFER_USER_COPY_FAILED) {
|
||||
iovec_iterator_set(iovec_iterator, iterator_offset_save);
|
||||
return RINGBUFFER_USER_COPY_FAILED;
|
||||
}
|
||||
|
||||
return total_done;
|
||||
}
|
||||
|
||||
size_t iovec_iterator_peek_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count, uintmax_t ringbuffer_offset) {
|
||||
size_t iterator_offset_save = iovec_iterator->total_offset;
|
||||
size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset);
|
||||
size_t total_done = 0;
|
||||
size_t current_done = 0;
|
||||
|
||||
for (;;) {
|
||||
size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
size_t copy_current = min(remaining_current, remaining_total);
|
||||
|
||||
current_done = ringbuffer_peek(ringbuffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), total_done, copy_current);
|
||||
if (current_done == 0 || current_done == RINGBUFFER_USER_COPY_FAILED)
|
||||
break;
|
||||
|
||||
iovec_iterator_skip(iovec_iterator, copy_current);
|
||||
total_done += current_done;
|
||||
|
||||
if (total_done == byte_count || iovec_iterator_finished(iovec_iterator))
|
||||
break;
|
||||
}
|
||||
|
||||
if (current_done == RINGBUFFER_USER_COPY_FAILED) {
|
||||
iovec_iterator_set(iovec_iterator, iterator_offset_save);
|
||||
return RINGBUFFER_USER_COPY_FAILED;
|
||||
}
|
||||
|
||||
return total_done;
|
||||
}
|
||||
|
||||
size_t iovec_iterator_copy_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count) {
|
||||
size_t done = iovec_iterator_peek_from_ringbuffer(iovec_iterator, ringbuffer, byte_count, 0);
|
||||
if (done == RINGBUFFER_USER_COPY_FAILED)
|
||||
return done;
|
||||
|
||||
ringbuffer_truncate(ringbuffer, done);
|
||||
return done;
|
||||
}
|
||||
|
||||
int iovec_iterator_next_page(iovec_iterator_t *iovec_iterator, void **page) {
|
||||
void *addr = (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset);
|
||||
if ((uintptr_t)addr % PAGE_SIZE)
|
||||
return EINVAL;
|
||||
|
||||
size_t remaining = iovec_iterator->current->len - iovec_iterator->current_offset;
|
||||
if (remaining < PAGE_SIZE)
|
||||
return EINVAL;
|
||||
|
||||
void *phys = vmm_getphysical(addr, true);
|
||||
if (phys == NULL)
|
||||
return EFAULT;
|
||||
|
||||
*page = phys;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user