blob: 08696edccf26b3003e850264403f00ebfaa3d70a [file]
/*
* FIXME: this is hack to override writeback without patch kernel.
* We should add proper interfaces to do this, instead. Then, remove
* this stuff.
*/
#if TUX3_FLUSHER == TUX3_FLUSHER_ASYNC_HACK
#include "tux3.h"
#include <linux/kthread.h>
#include <linux/freezer.h>
void tux3_set_mapping_bdi(struct inode *inode)
{
/*
* Hack: set backing_dev_info to use our bdi.
*/
inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi;
}
/*
* FIXME: dirty hack for now. We should add callback in writeback task
* instead of custom bdi.
*/
struct wb_writeback_work {
long nr_pages;
struct super_block *sb;
unsigned long *older_than_this;
enum writeback_sync_modes sync_mode;
unsigned int tagged_writepages:1;
unsigned int for_kupdate:1;
unsigned int range_cyclic:1;
unsigned int for_background:1;
unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */
enum wb_reason reason; /* why was writeback initiated? */
struct list_head list; /* pending work list */
struct completion *done; /* set if the caller waits */
};
/* Do the delta transition until specified delta */
static int try_delta_transition_until_delta(struct sb *sb, unsigned delta)
{
trace("delta %u, stage %u, backend_state %lx",
delta, sb->staging_delta, sb->backend_state);
/* Already delta transition was started for delta */
if (delta_after_eq(sb->staging_delta, delta))
return 1;
if (!test_and_set_bit(TUX3_COMMIT_RUNNING_BIT, &sb->backend_state)) {
/* Recheck after grabed TUX3_COMMIT_RUNNING_BIT */
if (delta_after_eq(sb->staging_delta, delta)) {
clear_bit(TUX3_COMMIT_RUNNING_BIT, &sb->backend_state);
return 1;
}
delta_transition(sb);
}
return delta_after_eq(sb->staging_delta, delta);
}
/* Advance delta transition until specified delta */
static int wait_for_transition(struct sb *sb, unsigned delta)
{
return wait_event_killable(sb->delta_event_wq,
try_delta_transition_until_delta(sb, delta));
}
static long tux3_wb_writeback(struct bdi_writeback *wb,
struct wb_writeback_work *work)
{
struct sb *sb = container_of(wb->bdi, struct sb, bdi);
struct delta_ref *delta_ref;
unsigned delta;
int err;
/* If we didn't finish replay yet, don't flush. */
if (!(vfs_sb(sb)->s_flags & MS_ACTIVE))
return 0;
/* Get delta that have to write */
delta_ref = delta_get(sb);
#ifdef UNIFY_DEBUG
/* NO_UNIFY and FORCE_UNIFY are not supported for now */
delta_ref->unify_flag = ALLOW_UNIFY;
#endif
delta = delta_ref->delta;
delta_put(sb, delta_ref);
/* Make sure the delta transition was done for current delta */
err = wait_for_transition(sb, delta);
if (err)
return err;
assert(delta_after_eq(sb->staging_delta, delta));
/* Wait for last referencer of delta was gone */
wait_event(sb->delta_event_wq,
test_bit(TUX3_COMMIT_PENDING_BIT, &sb->backend_state));
if (test_bit(TUX3_COMMIT_PENDING_BIT, &sb->backend_state)) {
clear_bit(TUX3_COMMIT_PENDING_BIT, &sb->backend_state);
err = flush_delta(sb);
/* FIXME: error handling */
#if 0
/* wb_update_bandwidth() is not exported to module */
wb_update_bandwidth(wb, wb_start);
#endif
}
return 1; /* FIXME: return code */
}
static bool inode_dirtied_after(struct inode *inode, unsigned long t)
{
bool ret = time_after(inode->dirtied_when, t);
#ifndef CONFIG_64BIT
/*
* For inodes being constantly redirtied, dirtied_when can get stuck.
* It _appears_ to be in the future, but is actually in distant past.
* This test is necessary to prevent such wrapped-around relative times
* from permanently stopping the whole bdi writeback.
*/
ret = ret && time_before_eq(inode->dirtied_when, jiffies);
#endif
return ret;
}
static int tux3_has_old_data(struct bdi_writeback *wb)
{
static unsigned int tux3_dirty_expire_interval = 30 * 100;
int has_old = 0;
/*
* We don't flush for each inodes. So, we flush all for each
* tux3_dirty_expire_interval.
*
* FIXME: we should pickup only older inodes?
*/
spin_lock(&wb->list_lock);
if (wb_has_dirty_io(wb)) {
unsigned long older_than_this = jiffies -
msecs_to_jiffies(tux3_dirty_expire_interval * 10);
struct inode *inode =
list_entry(wb->b_dirty.prev, struct inode, i_wb_list);
if (!inode_dirtied_after(inode, older_than_this))
has_old = 1;
}
spin_unlock(&wb->list_lock);
return has_old;
}
static long tux3_wb_check_old_data_flush(struct bdi_writeback *wb)
{
/* Hack: dirty_expire_interval is not exported to module */
unsigned long expired;
/*
* When set to zero, disable periodic writeback
*/
if (!dirty_writeback_interval)
return 0;
expired = wb->last_old_flush +
msecs_to_jiffies(dirty_writeback_interval * 10);
if (time_before(jiffies, expired))
return 0;
wb->last_old_flush = jiffies;
if (!tux3_has_old_data(wb)) {
/*
* If now after interval, we return 1 at least, to
* avoid to run tux3_wb_check_background_flush().
*/
return 1;
}
struct wb_writeback_work work = {
.nr_pages = 0,
.sync_mode = WB_SYNC_NONE,
.for_kupdate = 1,
.range_cyclic = 1,
.reason = WB_REASON_PERIODIC,
};
return tux3_wb_writeback(wb, &work);
}
static inline int tux3_over_bground_thresh(struct backing_dev_info *bdi,
long wrote)
{
/*
* FIXME: Memory pressure functions are not exported to module.
*
* So, if we didn't wrote any data on this wakeup, we assume
* this wakeup call is from memory pressure.
*/
return !wrote;
}
static long tux3_wb_check_background_flush(struct bdi_writeback *wb, long wrote)
{
if (tux3_over_bground_thresh(wb->bdi, wrote)) {
struct wb_writeback_work work = {
.nr_pages = LONG_MAX,
.sync_mode = WB_SYNC_NONE,
.for_background = 1,
.range_cyclic = 1,
.reason = WB_REASON_BACKGROUND,
};
return tux3_wb_writeback(wb, &work);
}
return 0;
}
static struct wb_writeback_work *
get_next_work_item(struct backing_dev_info *bdi)
{
struct wb_writeback_work *work = NULL;
spin_lock_bh(&bdi->wb_lock);
if (!list_empty(&bdi->work_list)) {
work = list_entry(bdi->work_list.next,
struct wb_writeback_work, list);
list_del_init(&work->list);
}
spin_unlock_bh(&bdi->wb_lock);
return work;
}
static long tux3_do_writeback(struct bdi_writeback *wb)
{
struct backing_dev_info *bdi = wb->bdi;
struct wb_writeback_work *work = NULL;
long wrote = 0;
set_bit(BDI_writeback_running, &wb->bdi->state);
while ((work = get_next_work_item(bdi)) != NULL) {
trace("nr_pages %ld, sb %p, sync_mode %d, "
"tagged_writepages %d, for_kupdate %d, range_cyclic %d, "
"for_background %d, reason %d, done %p",
work->nr_pages, work->sb, work->sync_mode,
work->tagged_writepages, work->for_kupdate,
work->range_cyclic, work->for_background,
work->reason, work->done);
wrote += tux3_wb_writeback(wb, work);
/*
* Notify the caller of completion if this is a synchronous
* work item, otherwise just free it.
*/
if (work->done)
complete(work->done);
else
kfree(work);
}
trace("flush done");
/*
* Check for periodic writeback, kupdated() style
*/
wrote += tux3_wb_check_old_data_flush(wb);
wrote += tux3_wb_check_background_flush(wb, wrote);
clear_bit(BDI_writeback_running, &wb->bdi->state);
return wrote;
}
/* Dirty hack to get bdi_wq address from module */
static struct workqueue_struct *kernel_bdi_wq;
/*
* Handle writeback of dirty data for the device backed by this bdi. Also
* reschedules periodically and does kupdated style flushing.
*/
static void tux3_writeback_workfn(struct work_struct *work)
{
struct bdi_writeback *wb = container_of(to_delayed_work(work),
struct bdi_writeback, dwork);
struct backing_dev_info *bdi = wb->bdi;
long pages_written;
#if 0
/* set_worker_desc() is not exported to module */
set_worker_desc("flush-tux3-%s", dev_name(bdi->dev));
#endif
current->flags |= PF_SWAPWRITE;
#if 0
/* current_is_workqueue_rescuer() is not exported to module */
if (likely(!current_is_workqueue_rescuer() ||
list_empty(&bdi->bdi_list)))
#endif
{
/*
* The normal path. Keep writing back @bdi until its
* work_list is empty. Note that this path is also taken
* if @bdi is shutting down even when we're running off the
* rescuer as work_list needs to be drained.
*/
do {
pages_written = tux3_do_writeback(wb);
// trace_writeback_pages_written(pages_written);
} while (!list_empty(&bdi->work_list));
}
#if 0
else {
/*
* bdi_wq can't get enough workers and we're running off
* the emergency worker. Don't hog it. Hopefully, 1024 is
* enough for efficient IO.
*/
pages_written = writeback_inodes_wb(&bdi->wb, 1024,
WB_REASON_FORKER_THREAD);
trace_writeback_pages_written(pages_written);
}
#endif
if (!list_empty(&bdi->work_list) ||
(wb_has_dirty_io(wb) && dirty_writeback_interval))
queue_delayed_work(kernel_bdi_wq, &wb->dwork,
msecs_to_jiffies(dirty_writeback_interval * 10));
current->flags &= ~PF_SWAPWRITE;
}
#include <linux/kallsyms.h>
static int tux3_setup_writeback(struct sb *sb, struct backing_dev_info *bdi)
{
/* Dirty hack to get bdi_wq address from module */
if (kernel_bdi_wq == NULL) {
unsigned long wq_addr;
wq_addr = kallsyms_lookup_name("bdi_wq");
if (!wq_addr) {
tux3_err(sb, "couldn't find bdi_wq address\n");
return -EINVAL;
}
kernel_bdi_wq = *(struct workqueue_struct **)wq_addr;
tux3_msg(sb, "use bdi_wq %p", kernel_bdi_wq);
}
/* Overwrite callback by ourself handler */
INIT_DELAYED_WORK(&bdi->wb.dwork, tux3_writeback_workfn);
return 0;
}
static int tux3_congested_fn(void *congested_data, int bdi_bits)
{
return bdi_congested(congested_data, bdi_bits);
}
/*
* We need to disable writeback to control dirty flags of inode.
* Otherwise, writeback will clear dirty, and inode can be reclaimed
* without our control.
*/
int tux3_init_flusher(struct sb *sb)
{
struct backing_dev_info *bdi = &sb->bdi;
int err;
bdi->ra_pages = vfs_sb(sb)->s_bdi->ra_pages;
bdi->congested_fn = tux3_congested_fn;
bdi->congested_data = vfs_sb(sb)->s_bdi;
err = bdi_setup_and_register(bdi, "tux3", BDI_CAP_MAP_COPY);
if (err)
return err;
err = tux3_setup_writeback(sb, bdi);
if (err) {
bdi_destroy(bdi);
return err;
}
vfs_sb(sb)->s_bdi = bdi;
return 0;
}
void tux3_exit_flusher(struct sb *sb)
{
struct backing_dev_info *bdi = vfs_sb(sb)->s_bdi;
if (bdi == &sb->bdi)
bdi_destroy(bdi);
}
static void schedule_flush_delta(struct sb *sb)
{
/* Wake up waiters for pending delta staging */
wake_up_all(&sb->delta_event_wq);
}
static void try_delta_transition(struct sb *sb)
{
#if 0
trace("stage %u, backend_state %lx",
sb->staging_delta, sb->backend_state);
sync_inodes_sb(vfs_sb(sb));
#endif
}
static int sync_current_delta(struct sb *sb, enum unify_flags unify_flag)
{
/* FORCE_UNIFY is not supported */
WARN_ON(unify_flag == FORCE_UNIFY);
/* This is called only for fsync, so we can take ->s_umount here */
down_read(&vfs_sb(sb)->s_umount);
sync_inodes_sb(vfs_sb(sb));
up_read(&vfs_sb(sb)->s_umount);
return 0; /* FIXME: error code */
}
#endif /* TUX3_FLUSHER != TUX3_FLUSHER_ASYNC_HACK */