dev: implement reading from block devices

reading from block devices is done using the block cache (bcache).
This cache stores sectors from a block device in pages of memory
marked as 'cached', which will allow them to be reclaimed when
memory pressure is high (TODO).

while block device drivers implement callbacks allowing reading/writing
at block-granularity, the device subsystem uses the block cache to
implement reading/writing at byte-granularity in a driver-agnostic way.

block drivers can disable the block cache for their devices, but this
will require that any clients communicate with the devices at
block-granularity.

also added an offset parameter to device and object read/write functions/callbacks.
This commit is contained in:
2023-07-09 21:58:40 +01:00
parent 53440653f2
commit 3233169f25
14 changed files with 435 additions and 52 deletions

46
include/socks/block.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef SOCKS_BLOCK_H_
#define SOCKS_BLOCK_H_
#include <socks/types.h>
#include <socks/btree.h>
#include <socks/locks.h>
#include <socks/status.h>
#include <stdbool.h>
enum block_device_flags {
BLOCK_DEVICE_NO_BCACHE = 0x01u,
};
struct bcache {
unsigned int b_sector_size;
unsigned int b_sectors_per_page;
struct btree b_pagetree;
spin_lock_t b_lock;
};
struct bcache_sector {
struct vm_page *sect_page;
unsigned int sect_index;
void *sect_buf;
bool sect_present;
};
extern struct bcache *bcache_create(unsigned int block_size);
extern void bcache_destroy(struct bcache *cache);
extern kern_status_t bcache_init(struct bcache *cache, unsigned int block_size);
extern void bcache_deinit(struct bcache *cache);
static inline void bcache_lock(struct bcache *cache)
{
spin_lock(&cache->b_lock);
}
static inline void bcache_unlock(struct bcache *cache)
{
spin_unlock(&cache->b_lock);
}
extern kern_status_t bcache_get(struct bcache *cache, sectors_t at, bool create, struct bcache_sector *out);
extern void bcache_mark_present(struct bcache_sector *sect);
#endif

View File

@@ -6,6 +6,7 @@
#include <socks/status.h>
#include <socks/bitmap.h>
#include <socks/object.h>
#include <socks/block.h>
#include <socks/fb.h>
#include <socks/ringbuffer.h>
@@ -49,14 +50,14 @@ struct iovec {
};
struct device_type_ops {
kern_status_t(*read)(struct device *, void *, size_t, size_t *, socks_flags_t);
kern_status_t(*write)(struct device *, const void *, size_t, size_t *, socks_flags_t);
kern_status_t(*read)(struct device *, void *, size_t, size_t, size_t *, socks_flags_t);
kern_status_t(*write)(struct device *, const void *, size_t, size_t, size_t *, socks_flags_t);
kern_status_t(*register_device)(struct device *);
};
struct block_device_ops {
kern_status_t(*read_blocks)(struct device *, sectors_t, size_t, struct iovec *, size_t, socks_flags_t);
kern_status_t(*write_blocks)(struct device *, sectors_t, size_t, struct iovec *, size_t, socks_flags_t);
kern_status_t(*read_blocks)(struct device *, sectors_t, size_t *, struct iovec *, size_t, socks_flags_t);
kern_status_t(*write_blocks)(struct device *, sectors_t, size_t *, struct iovec *, size_t, socks_flags_t);
kern_status_t(*ioctl)(struct device *, unsigned int, void *);
};
@@ -68,8 +69,8 @@ struct net_device_ops {
};
struct char_device_ops {
kern_status_t(*read)(struct device *, void *, size_t, size_t *, socks_flags_t);
kern_status_t(*write)(struct device *, const void *, size_t, size_t *, socks_flags_t);
kern_status_t(*read)(struct device *, void *, size_t, size_t, size_t *, socks_flags_t);
kern_status_t(*write)(struct device *, const void *, size_t, size_t, size_t *, socks_flags_t);
};
struct input_device_ops {
@@ -86,9 +87,11 @@ struct framebuffer_device_ops {
struct block_device {
struct block_device_ops *b_ops;
struct bcache b_cache;
enum block_device_flags b_flags;
unsigned int b_id;
unsigned int sector_size;
sectors_t capacity;
unsigned int b_sector_size;
sectors_t b_capacity;
};
struct char_device {
@@ -195,8 +198,8 @@ static inline void device_unlock_irqrestore(struct device *dev, unsigned long fl
object_unlock_irqrestore(&dev->dev_base, flags);
}
extern kern_status_t device_read(struct device *dev, void *buf, size_t size, size_t *bytes_read, socks_flags_t flags);
extern kern_status_t device_write(struct device *dev, const void *buf, size_t size, size_t *bytes_written, socks_flags_t flags);
extern kern_status_t device_read(struct device *dev, void *buf, size_t offset, size_t size, size_t *bytes_read, socks_flags_t flags);
extern kern_status_t device_write(struct device *dev, const void *buf, size_t offset, size_t size, size_t *bytes_written, socks_flags_t flags);
extern struct device *cast_to_device(struct object *obj);
@@ -286,7 +289,8 @@ static inline void device_deref(struct device *dev)
}
extern kern_status_t input_device_report_event(struct input_device *dev, const struct input_event *ev, bool noblock);
extern kern_status_t input_device_read(struct device *dev, void *buf, size_t size, size_t *bytes_read, socks_flags_t flags);
extern kern_status_t input_device_read(struct device *dev, void *buf, size_t offset,
size_t size, size_t *bytes_read, socks_flags_t flags);
extern kern_status_t input_device_add_hook(struct device *dev, struct input_event_hook *hook);
extern kern_status_t input_device_remove_hook(struct device *dev, struct input_event_hook *hook);

View File

@@ -32,8 +32,8 @@ enum object_type_flags {
struct object_ops {
kern_status_t(*open)(struct object *obj);
kern_status_t(*close)(struct object *obj);
kern_status_t(*read)(struct object *obj, void *p, size_t *r, socks_flags_t flags);
kern_status_t(*write)(struct object *obj, const void *p, size_t *w, socks_flags_t flags);
kern_status_t(*read)(struct object *obj, void *p, size_t off, size_t *r, socks_flags_t flags);
kern_status_t(*write)(struct object *obj, const void *p, size_t off, size_t *w, socks_flags_t flags);
kern_status_t(*destroy)(struct object *obj);
kern_status_t(*query_name)(struct object *obj, char out[OBJECT_NAME_MAX]);
kern_status_t(*parse)(struct object *obj, const char *path, struct object **out);
@@ -92,8 +92,8 @@ static inline kern_status_t object_get(const char *path, struct object **out)
{
return object_namespace_get_object(global_namespace(), path, out);
}
extern kern_status_t object_read(struct object *obj, void *p, size_t max, size_t *nr_read, socks_flags_t flags);
extern kern_status_t object_write(struct object *obj, const void *p, size_t max, size_t *nr_written, socks_flags_t flags);
extern kern_status_t object_read(struct object *obj, void *p, size_t offset, size_t max, size_t *nr_read, socks_flags_t flags);
extern kern_status_t object_write(struct object *obj, const void *p, size_t offset, size_t max, size_t *nr_written, socks_flags_t flags);
extern kern_status_t object_get_child_named(struct object *obj, const char *name, struct object **out);
extern kern_status_t object_get_child_at(struct object *obj, size_t at, struct object **out);
extern kern_status_t object_query_name(struct object *obj, char name[OBJECT_NAME_MAX]);

View File

@@ -131,8 +131,8 @@ static inline struct driver *tty_driver_base(struct tty_driver *drv)
return &drv->tty_base;
}
extern kern_status_t tty_read(struct device *tty, void *buf, size_t max, size_t *nr_read, socks_flags_t flags);
extern kern_status_t tty_write(struct device *tty, const void *buf, size_t len, size_t *nr_written, socks_flags_t flags);
extern kern_status_t tty_read(struct device *tty, void *buf, size_t offset, size_t max, size_t *nr_read, socks_flags_t flags);
extern kern_status_t tty_write(struct device *tty, const void *buf, size_t offset, size_t len, size_t *nr_written, socks_flags_t flags);
extern kern_status_t tty_report_event(struct device *tty, const struct input_event *ev);
#ifdef __cplusplus

View File

@@ -5,6 +5,8 @@
#include <socks/types.h>
#include <socks/status.h>
#include <socks/queue.h>
#include <socks/btree.h>
#include <socks/bitmap.h>
#include <socks/locks.h>
#include <socks/machine/vm.h>
@@ -12,6 +14,8 @@
extern "C" {
#endif
struct bcache;
/* maximum number of NUMA nodes */
#define VM_MAX_NODES 64
/* maximum number of memory zones per node */
@@ -21,6 +25,11 @@ extern "C" {
/* maximum number of sparse memory sectors */
#define VM_MAX_SECTORS 1024
/* maximum number of disk sectors that can be stored in a single
page. AKA the number of bits in the sector bitmap.
used by the block cache */
#define VM_MAX_SECTORS_PER_PAGE 32
#define VM_CHECK_ALIGN(p, mask) ((((p) & (mask)) == (p)) ? 1 : 0)
#define VM_CACHE_INITIALISED(c) ((c)->c_obj_count != 0)
@@ -108,6 +117,8 @@ enum vm_page_flags {
VM_PAGE_HEAD = 0x04u,
/* page is part of a huge-page */
VM_PAGE_HUGE = 0x08u,
/* page is holding cached data from secondary storage, and can be freed if necessary (and not dirty). */
VM_PAGE_CACHE = 0x10u,
};
enum vm_memory_region_status {
@@ -210,18 +221,40 @@ struct vm_page {
uint32_t p_flags;
/* multi-purpose list.
/* owner-specific pointer */
union {
struct vm_slab *p_slab;
struct bcache *p_bcache;
void *p_priv0;
};
/* multi-purpose list/tree entry.
the owner of the page can decide what to do with this.
some examples:
- the buddy allocator uses this to maintain its per-zone free-page lists.
- the block cache uses this to maintain a tree of pages keyed by block number.
*/
struct queue_entry p_list;
/* owner-specific data */
union {
struct vm_slab *p_slab;
struct queue_entry p_list;
struct btree_node p_bnode;
/* btree_node contains three pointers, so provide three pointer-sized integers for
use if p_bnode isn't needed. */
uintptr_t priv1[3];
};
union {
/* used by bcache when sector size is < page size. bitmap of present/missing sectors */
DECLARE_BITMAP(p_blockbits, VM_MAX_SECTORS_PER_PAGE);
uint32_t p_priv2;
};
union {
/* sector address, used by bcache */
sectors_t p_blockid;
uint32_t p_priv3[2];
};
} __attribute__((aligned(2 * sizeof(unsigned long))));
/* represents a sector of memory, containing its own array of vm_pages.