Skip to content

Commit

Permalink
writeback: Writeback rework
Browse files Browse the repository at this point in the history
Completely rework writeback functionality. It's now much more stable,
although still synchronous. New file_ops callbacks have been added
(writepages and fsyncdata).

Signed-off-by: Pedro Falcato <[email protected]>
  • Loading branch information
heatd committed Dec 31, 2023
1 parent 0418cb1 commit 88e6469
Show file tree
Hide file tree
Showing 29 changed files with 895 additions and 334 deletions.
8 changes: 6 additions & 2 deletions kernel/include/onyx/block.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016 - 2022 Pedro Falcato
* Copyright (c) 2016 - 2023 Pedro Falcato
* This file is part of Onyx, and is released under the terms of the MIT License
* check LICENSE at the root directory for more information
*
Expand All @@ -15,6 +15,7 @@
#include <onyx/culstring.h>
#include <onyx/dev.h>
#include <onyx/list.h>
#include <onyx/mm/flush.h>
#include <onyx/page.h>
#include <onyx/page_iov.h>
#include <onyx/types.h>
Expand Down Expand Up @@ -89,6 +90,8 @@ struct blockdev
// An optional partition prefix, like the 'p' in nvme0n1p1
cul::string partition_prefix;

unique_ptr<flush::writeback_dev> wbdev{nullptr};

constexpr blockdev() = default;
};

Expand Down Expand Up @@ -178,7 +181,8 @@ unique_ptr<blockdev> blkdev_create_scsi_like_dev();
// Read-write user and group, no permissions to others
#define BLOCK_DEVICE_PERMISSIONS 0660

struct blockdev;
void partition_setup_disk(struct blockdev *dev);

flush::writeback_dev *bdev_get_wbdev(struct inode *ino);

#endif
83 changes: 78 additions & 5 deletions kernel/include/onyx/buffer.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/*
* Copyright (c) 2020 Pedro Falcato
* Copyright (c) 2020 - 2023 Pedro Falcato
* This file is part of Onyx, and is released under the terms of the MIT License
* check LICENSE at the root directory for more information
*
* SPDX-License-Identifier: MIT
*/
#ifndef _ONYX_BUFFER_H
#define _ONYX_BUFFER_H
Expand All @@ -16,6 +18,8 @@
* It's supposed to be used by filesystems only, for metadata.
*/

struct vm_object;

struct block_buf
{
/* This block's refcount */
Expand All @@ -34,19 +38,44 @@ struct block_buf
sector_t block_nr;
/* The block size */
unsigned int block_size;
struct list_head assoc_buffers_node;
struct vm_object *assoc_buffers_obj;
};

#define BLOCKBUF_FLAG_DIRTY (1 << 0)
#define BLOCKBUF_FLAG_UNDER_WB (1 << 1)

static inline bool bb_test_and_set(struct block_buf *buf, unsigned int flag)
{
unsigned int old;
do
{
old = __atomic_load_n(&buf->flags, __ATOMIC_ACQUIRE);
if (old & flag)
return false;
} while (!__atomic_compare_exchange_n(&buf->flags, &old, old | flag, false, __ATOMIC_RELEASE,
__ATOMIC_RELAXED));
return true;
}

static inline bool bb_test_and_clear(struct block_buf *buf, unsigned int flag)
{
return __atomic_fetch_and(&buf->flags, ~flag, __ATOMIC_RELEASE) & flag;
}

static inline void bb_clear_flag(struct block_buf *buf, unsigned int flag)
{
__atomic_and_fetch(&buf->flags, ~flag, __ATOMIC_RELEASE);
}

#define MAX_BLOCK_SIZE PAGE_SIZE

struct superblock;

struct block_buf *page_add_blockbuf(struct page *page, unsigned int page_off);
struct block_buf *sb_read_block(const struct superblock *sb, unsigned long block);
void block_buf_free(struct block_buf *buf);
void block_buf_writeback(struct block_buf *buf);
void block_buf_sync(struct block_buf *buf);
void block_buf_dirty(struct block_buf *buf);
struct block_buf *block_buf_from_page(struct page *p);
void page_destroy_block_bufs(struct page *page);
Expand All @@ -71,6 +100,40 @@ static inline void *block_buf_data(struct block_buf *b)
return (void *) (((unsigned long) PAGE_TO_VIRT(b->this_page)) + b->page_off);
}

/**
* @brief Associate a block_buf with a vm_object
* This is used for e.g indirect blocks that want to be written back
* when doing fsync. The vm_object does *not* need to be the block device's.
*
* @param buf Block buf
* @param object Object
*/
void block_buf_associate(struct block_buf *buf, struct vm_object *object);

/**
* @brief Sync all the associated buffers to this vm_object
*
* @param object VM object (of probably an fs's inode)
*/
void block_buf_sync_assoc(struct vm_object *object);

/**
* @brief Dirty a block buffer and associate it with an inode
* The association will allow us to write this buffer back when syncing
* the inode's data.
*
* @param buf Buffer to dirty
* @param inode Inode to add it to
*/
void block_buf_dirty_inode(struct block_buf *buf, struct inode *inode);

/**
* @brief Tear down a vm object's assoc list
*
* @param object Object to tear down
*/
void block_buf_tear_down_assoc(struct vm_object *object);

#ifdef __cplusplus

void page_remove_block_buf(struct page *page, size_t offset, size_t end);
Expand Down Expand Up @@ -177,22 +240,32 @@ class buf_dirty_trigger
{
private:
auto_block_buf &buf;
struct inode *inode{nullptr};
bool dont_dirty;

void do_dirty()
{
if (inode)
block_buf_dirty_inode(buf, inode);
else
block_buf_dirty(buf);
}

public:
buf_dirty_trigger(auto_block_buf &b) : buf{b}, dont_dirty{false}
buf_dirty_trigger(auto_block_buf &b, struct inode *inode = nullptr)
: buf{b}, inode{inode}, dont_dirty{false}
{
}

~buf_dirty_trigger()
{
if (!dont_dirty)
block_buf_dirty(buf);
do_dirty();
}

void explicit_dirty()
{
block_buf_dirty(buf);
do_dirty();
dont_dirty = true;
}

Expand Down
25 changes: 24 additions & 1 deletion kernel/include/onyx/filemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

#include <onyx/iovec_iter.h>
#include <onyx/types.h>
#include <onyx/vfs.h>

struct file;
struct inode;
struct page;

/**
Expand Down Expand Up @@ -46,4 +46,27 @@ ssize_t filemap_write_iter(struct file *filp, size_t off, iovec_iter *iter, unsi

int filemap_find_page(struct inode *ino, size_t pgoff, unsigned int flags, struct page **outp);

void page_start_writeback(struct page *page, struct inode *inode)
EXCLUDES(inode->i_pages->page_lock) REQUIRES(page);

void page_end_writeback(struct page *page, struct inode *inode) EXCLUDES(inode->i_pages->page_lock)
REQUIRES(page);

/**
* @brief Marks a page dirty in the filemap
*
* @param ino Inode to mark dirty
* @param page Page to mark dirty
* @param pgoff Page offset
* @invariant page is locked
*/
void filemap_mark_dirty(struct inode *ino, struct page *page, size_t pgoff) REQUIRES(page);

struct writepages_info;

int filemap_writepages(struct inode *inode, struct writepages_info *wpinfo);

#define FILEMAP_MARK_DIRTY RA_MARK_0
#define FILEMAP_MARK_WRITEBACK RA_MARK_1

#endif
3 changes: 2 additions & 1 deletion kernel/include/onyx/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ static inline void list_copy(struct list_head *dest, const struct list_head *src

static inline void list_move(struct list_head *dest, struct list_head *src)
{
list_copy(dest, src);
if (!list_is_empty(src))
list_copy(dest, src);
list_reset(src);
}

Expand Down
79 changes: 27 additions & 52 deletions kernel/include/onyx/mm/flush.h
Original file line number Diff line number Diff line change
@@ -1,106 +1,81 @@
/*
* Copyright (c) 2019 Pedro Falcato
* Copyright (c) 2019 - 2023 Pedro Falcato
* This file is part of Onyx, and is released under the terms of the MIT License
* check LICENSE at the root directory for more information
*
* SPDX-License-Identifier: MIT
*/

#ifndef _ONYX_MM_FLUSH_H
#define _ONYX_MM_FLUSH_H
#ifndef _ONYX_MM_WRITEBACK_H
#define _ONYX_MM_WRITEBACK_H

#include <onyx/list.h>
#include <onyx/mutex.h>
#include <onyx/semaphore.h>
#include <onyx/spinlock.h>
#include <onyx/vm.h>

/* TODO: This file started as mm specific but it's quite fs now, no? */

struct inode;

struct flush_object;
/* Implemented by users of the flush subsystem */
struct flush_ops
{
ssize_t (*flush)(struct flush_object *fmd);
bool (*is_dirty)(struct flush_object *fmd);
void (*set_dirty)(bool value, struct flush_object *fmd);
};

struct flush_object
{
struct list_head dirty_list;
void *blk_list;
const struct flush_ops *ops;
};
struct blockdev;

/* Keep C APIs here */

void flush_init(void);
void flush_add_buf(struct flush_object *blk);
void flush_remove_buf(struct flush_object *blk);
void flush_add_inode(struct inode *ino);
void flush_remove_inode(struct inode *ino);
ssize_t flush_sync_one(struct flush_object *obj);
void flush_do_sync(void);

#define WB_FLAG_SYNC (1 << 0)

#ifdef __cplusplus

#include <onyx/atomic.hpp>

namespace flush
{

class flush_dev
class writeback_dev
{
private:
/* Each flush dev has a list of dirty bufs that need flushing. */
struct list_head dirty_bufs;
/* Each writeback dev has a list of dirty inodes that need flushing. */
struct list_head dirty_inodes;
atomic<unsigned long> block_load;
struct mutex __lock;
struct spinlock __lock;
/* Each flush dev also is associated with a thread that runs every x seconds */
struct thread *thread;
struct semaphore thread_sem;
struct blockdev *bdev;
struct list_head wbdev_list_node;

public:
static constexpr unsigned long wb_run_delta_ms = 10000;
constexpr flush_dev()
: dirty_bufs{}, dirty_inodes{}, block_load{0}, __lock{}, thread{}, thread_sem{}
constexpr writeback_dev(struct blockdev *bdev)
: dirty_inodes{}, thread{}, thread_sem{}, bdev{bdev}
{
mutex_init(&__lock);
INIT_LIST_HEAD(&dirty_bufs);
INIT_LIST_HEAD(&dirty_inodes);
}

~flush_dev()
{
}

unsigned long get_load()
{
return block_load;
}
~writeback_dev() = default;

void lock() ACQUIRE(__lock)
void lock()
{
mutex_lock(&__lock);
spin_lock(&__lock);
}

void unlock() RELEASE(__lock)
void unlock()
{
mutex_unlock(&__lock);
spin_unlock(&__lock);
}

bool called_from_sync();

void init();
void run();
bool add_buf(struct flush_object *buf);
void remove_buf(struct flush_object *buf);
void add_inode(struct inode *ino);
void remove_inode(struct inode *ino);
void sync();
ssize_t sync_one(struct flush_object *obj);
void sync(unsigned int flags);
void end_inode_writeback(struct inode *ino);

static writeback_dev *from_list_head(struct list_head *l)
{
return container_of(l, writeback_dev, wbdev_list_node);
}
};

}; // namespace flush
Expand Down
4 changes: 4 additions & 0 deletions kernel/include/onyx/mm/vm_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ struct vm_object

struct vm_object *prev_private{nullptr}, *next_private{nullptr};

/* See fs/buffer.cpp for example usage of these struct members */
struct spinlock private_lock;
struct list_head private_list;

vm_object();
~vm_object();

Expand Down
Loading

0 comments on commit 88e6469

Please sign in to comment.