Commit a12e4d30 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'writeback' of git://git.kernel.dk/linux-2.6-block

* 'writeback' of git://git.kernel.dk/linux-2.6-block:
  writeback: check for registered bdi in flusher add and inode dirty
  writeback: add name to backing_dev_info
  writeback: add some debug inode list counters to bdi stats
  writeback: get rid of pdflush completely
  writeback: switch to per-bdi threads for flushing data
  writeback: move dirty inodes from super_block to backing_dev_info
  writeback: get rid of generic_sync_sb_inodes() export
parents 89af571c 500b067c
...@@ -501,6 +501,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) ...@@ -501,6 +501,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
(VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
q->backing_dev_info.state = 0; q->backing_dev_info.state = 0;
q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY; q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY;
q->backing_dev_info.name = "block";
err = bdi_init(&q->backing_dev_info); err = bdi_init(&q->backing_dev_info);
if (err) { if (err) {
......
...@@ -268,6 +268,7 @@ aoeblk_gdalloc(void *vp) ...@@ -268,6 +268,7 @@ aoeblk_gdalloc(void *vp)
if (!d->blkq) if (!d->blkq)
goto err_mempool; goto err_mempool;
blk_queue_make_request(d->blkq, aoeblk_make_request); blk_queue_make_request(d->blkq, aoeblk_make_request);
d->blkq->backing_dev_info.name = "aoe";
if (bdi_init(&d->blkq->backing_dev_info)) if (bdi_init(&d->blkq->backing_dev_info))
goto err_blkq; goto err_blkq;
spin_lock_irqsave(&d->lock, flags); spin_lock_irqsave(&d->lock, flags);
......
...@@ -822,6 +822,7 @@ static const struct file_operations zero_fops = { ...@@ -822,6 +822,7 @@ static const struct file_operations zero_fops = {
* - permits private mappings, "copies" are taken of the source of zeros * - permits private mappings, "copies" are taken of the source of zeros
*/ */
static struct backing_dev_info zero_bdi = { static struct backing_dev_info zero_bdi = {
.name = "char/mem",
.capabilities = BDI_CAP_MAP_COPY, .capabilities = BDI_CAP_MAP_COPY,
}; };
......
...@@ -1950,14 +1950,7 @@ static int pohmelfs_get_sb(struct file_system_type *fs_type, ...@@ -1950,14 +1950,7 @@ static int pohmelfs_get_sb(struct file_system_type *fs_type,
*/ */
static void pohmelfs_kill_super(struct super_block *sb) static void pohmelfs_kill_super(struct super_block *sb)
{ {
struct writeback_control wbc = { sync_inodes_sb(sb);
.sync_mode = WB_SYNC_ALL,
.range_start = 0,
.range_end = LLONG_MAX,
.nr_to_write = LONG_MAX,
};
generic_sync_sb_inodes(sb, &wbc);
kill_anon_super(sb); kill_anon_super(sb);
} }
......
...@@ -1352,6 +1352,7 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi) ...@@ -1352,6 +1352,7 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
{ {
int err; int err;
bdi->name = "btrfs";
bdi->capabilities = BDI_CAP_MAP_COPY; bdi->capabilities = BDI_CAP_MAP_COPY;
err = bdi_init(bdi); err = bdi_init(bdi);
if (err) if (err)
......
...@@ -281,7 +281,7 @@ static void free_more_memory(void) ...@@ -281,7 +281,7 @@ static void free_more_memory(void)
struct zone *zone; struct zone *zone;
int nid; int nid;
wakeup_pdflush(1024); wakeup_flusher_threads(1024);
yield(); yield();
for_each_online_node(nid) { for_each_online_node(nid) {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* - no readahead or I/O queue unplugging required * - no readahead or I/O queue unplugging required
*/ */
struct backing_dev_info directly_mappable_cdev_bdi = { struct backing_dev_info directly_mappable_cdev_bdi = {
.name = "char",
.capabilities = ( .capabilities = (
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
/* permit private copies of the data to be taken */ /* permit private copies of the data to be taken */
......
...@@ -51,6 +51,7 @@ static const struct address_space_operations configfs_aops = { ...@@ -51,6 +51,7 @@ static const struct address_space_operations configfs_aops = {
}; };
static struct backing_dev_info configfs_backing_dev_info = { static struct backing_dev_info configfs_backing_dev_info = {
.name = "configfs",
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
}; };
......
This diff is collapsed.
...@@ -801,6 +801,7 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) ...@@ -801,6 +801,7 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
{ {
int err; int err;
fc->bdi.name = "fuse";
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn; fc->bdi.unplug_io_fn = default_unplug_io_fn;
/* fuse does it's own writeback accounting */ /* fuse does it's own writeback accounting */
......
...@@ -44,6 +44,7 @@ static const struct inode_operations hugetlbfs_dir_inode_operations; ...@@ -44,6 +44,7 @@ static const struct inode_operations hugetlbfs_dir_inode_operations;
static const struct inode_operations hugetlbfs_inode_operations; static const struct inode_operations hugetlbfs_inode_operations;
static struct backing_dev_info hugetlbfs_backing_dev_info = { static struct backing_dev_info hugetlbfs_backing_dev_info = {
.name = "hugetlbfs",
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
}; };
......
...@@ -879,6 +879,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * ...@@ -879,6 +879,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
server->rsize = NFS_MAX_FILE_IO_SIZE; server->rsize = NFS_MAX_FILE_IO_SIZE;
server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
server->backing_dev_info.name = "nfs";
server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD;
if (server->wsize > max_rpc_payload) if (server->wsize > max_rpc_payload)
......
...@@ -325,6 +325,7 @@ static void dlmfs_clear_inode(struct inode *inode) ...@@ -325,6 +325,7 @@ static void dlmfs_clear_inode(struct inode *inode)
} }
static struct backing_dev_info dlmfs_backing_dev_info = { static struct backing_dev_info dlmfs_backing_dev_info = {
.name = "ocfs2-dlmfs",
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
}; };
......
...@@ -46,6 +46,7 @@ static const struct super_operations ramfs_ops; ...@@ -46,6 +46,7 @@ static const struct super_operations ramfs_ops;
static const struct inode_operations ramfs_dir_inode_operations; static const struct inode_operations ramfs_dir_inode_operations;
static struct backing_dev_info ramfs_backing_dev_info = { static struct backing_dev_info ramfs_backing_dev_info = {
.name = "ramfs",
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK |
BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY |
......
...@@ -62,9 +62,6 @@ static struct super_block *alloc_super(struct file_system_type *type) ...@@ -62,9 +62,6 @@ static struct super_block *alloc_super(struct file_system_type *type)
s = NULL; s = NULL;
goto out; goto out;
} }
INIT_LIST_HEAD(&s->s_dirty);
INIT_LIST_HEAD(&s->s_io);
INIT_LIST_HEAD(&s->s_more_io);
INIT_LIST_HEAD(&s->s_files); INIT_LIST_HEAD(&s->s_files);
INIT_LIST_HEAD(&s->s_instances); INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon); INIT_HLIST_HEAD(&s->s_anon);
...@@ -171,7 +168,7 @@ int __put_super_and_need_restart(struct super_block *sb) ...@@ -171,7 +168,7 @@ int __put_super_and_need_restart(struct super_block *sb)
* Drops a temporary reference, frees superblock if there's no * Drops a temporary reference, frees superblock if there's no
* references left. * references left.
*/ */
static void put_super(struct super_block *sb) void put_super(struct super_block *sb)
{ {
spin_lock(&sb_lock); spin_lock(&sb_lock);
__put_super(sb); __put_super(sb);
......
...@@ -19,20 +19,22 @@ ...@@ -19,20 +19,22 @@
SYNC_FILE_RANGE_WAIT_AFTER) SYNC_FILE_RANGE_WAIT_AFTER)
/* /*
* Do the filesystem syncing work. For simple filesystems sync_inodes_sb(sb, 0) * Do the filesystem syncing work. For simple filesystems
* just dirties buffers with inodes so we have to submit IO for these buffers * writeback_inodes_sb(sb) just dirties buffers with inodes so we have to
* via __sync_blockdev(). This also speeds up the wait == 1 case since in that * submit IO for these buffers via __sync_blockdev(). This also speeds up the
* case write_inode() functions do sync_dirty_buffer() and thus effectively * wait == 1 case since in that case write_inode() functions do
* write one block at a time. * sync_dirty_buffer() and thus effectively write one block at a time.
*/ */
static int __sync_filesystem(struct super_block *sb, int wait) static int __sync_filesystem(struct super_block *sb, int wait)
{ {
/* Avoid doing twice syncing and cache pruning for quota sync */ /* Avoid doing twice syncing and cache pruning for quota sync */
if (!wait) if (!wait) {
writeout_quota_sb(sb, -1); writeout_quota_sb(sb, -1);
else writeback_inodes_sb(sb);
} else {
sync_quota_sb(sb, -1); sync_quota_sb(sb, -1);
sync_inodes_sb(sb, wait); sync_inodes_sb(sb);
}
if (sb->s_op->sync_fs) if (sb->s_op->sync_fs)
sb->s_op->sync_fs(sb, wait); sb->s_op->sync_fs(sb, wait);
return __sync_blockdev(sb->s_bdev, wait); return __sync_blockdev(sb->s_bdev, wait);
...@@ -118,7 +120,7 @@ static void sync_filesystems(int wait) ...@@ -118,7 +120,7 @@ static void sync_filesystems(int wait)
*/ */
SYSCALL_DEFINE0(sync) SYSCALL_DEFINE0(sync)
{ {
wakeup_pdflush(0); wakeup_flusher_threads(0);
sync_filesystems(0); sync_filesystems(0);
sync_filesystems(1); sync_filesystems(1);
if (unlikely(laptop_mode)) if (unlikely(laptop_mode))
......
...@@ -31,6 +31,7 @@ static const struct address_space_operations sysfs_aops = { ...@@ -31,6 +31,7 @@ static const struct address_space_operations sysfs_aops = {
}; };
static struct backing_dev_info sysfs_backing_dev_info = { static struct backing_dev_info sysfs_backing_dev_info = {
.name = "sysfs",
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
}; };
......
...@@ -65,26 +65,14 @@ ...@@ -65,26 +65,14 @@
static int shrink_liability(struct ubifs_info *c, int nr_to_write) static int shrink_liability(struct ubifs_info *c, int nr_to_write)
{ {
int nr_written; int nr_written;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE,
.range_end = LLONG_MAX,
.nr_to_write = nr_to_write,
};
generic_sync_sb_inodes(c->vfs_sb, &wbc);
nr_written = nr_to_write - wbc.nr_to_write;
nr_written = writeback_inodes_sb(c->vfs_sb);
if (!nr_written) { if (!nr_written) {
/* /*
* Re-try again but wait on pages/inodes which are being * Re-try again but wait on pages/inodes which are being
* written-back concurrently (e.g., by pdflush). * written-back concurrently (e.g., by pdflush).
*/ */
memset(&wbc, 0, sizeof(struct writeback_control)); nr_written = sync_inodes_sb(c->vfs_sb);
wbc.sync_mode = WB_SYNC_ALL;
wbc.range_end = LLONG_MAX;
wbc.nr_to_write = nr_to_write;
generic_sync_sb_inodes(c->vfs_sb, &wbc);
nr_written = nr_to_write - wbc.nr_to_write;
} }
dbg_budg("%d pages were written back", nr_written); dbg_budg("%d pages were written back", nr_written);
......
...@@ -438,12 +438,6 @@ static int ubifs_sync_fs(struct super_block *sb, int wait) ...@@ -438,12 +438,6 @@ static int ubifs_sync_fs(struct super_block *sb, int wait)
{ {
int i, err; int i, err;
struct ubifs_info *c = sb->s_fs_info; struct ubifs_info *c = sb->s_fs_info;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.range_start = 0,
.range_end = LLONG_MAX,
.nr_to_write = LONG_MAX,
};
/* /*
* Zero @wait is just an advisory thing to help the file system shove * Zero @wait is just an advisory thing to help the file system shove
...@@ -462,7 +456,7 @@ static int ubifs_sync_fs(struct super_block *sb, int wait) ...@@ -462,7 +456,7 @@ static int ubifs_sync_fs(struct super_block *sb, int wait)
* the user be able to get more accurate results of 'statfs()' after * the user be able to get more accurate results of 'statfs()' after
* they synchronize the file system. * they synchronize the file system.
*/ */
generic_sync_sb_inodes(sb, &wbc); sync_inodes_sb(sb);
/* /*
* Synchronize write buffers, because 'ubifs_run_commit()' does not * Synchronize write buffers, because 'ubifs_run_commit()' does not
...@@ -1971,6 +1965,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1971,6 +1965,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
* *
* Read-ahead will be disabled because @c->bdi.ra_pages is 0. * Read-ahead will be disabled because @c->bdi.ra_pages is 0.
*/ */
c->bdi.name = "ubifs",
c->bdi.capabilities = BDI_CAP_MAP_COPY; c->bdi.capabilities = BDI_CAP_MAP_COPY;
c->bdi.unplug_io_fn = default_unplug_io_fn; c->bdi.unplug_io_fn = default_unplug_io_fn;
err = bdi_init(&c->bdi); err = bdi_init(&c->bdi);
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/proportions.h> #include <linux/proportions.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h>
#include <linux/writeback.h>
#include <asm/atomic.h> #include <asm/atomic.h>
struct page; struct page;
...@@ -23,9 +25,11 @@ struct dentry; ...@@ -23,9 +25,11 @@ struct dentry;
* Bits in backing_dev_info.state * Bits in backing_dev_info.state
*/ */
enum bdi_state { enum bdi_state {
BDI_pdflush, /* A pdflush thread is working this device */ BDI_pending, /* On its way to being activated */
BDI_wb_alloc, /* Default embedded wb allocated */
BDI_async_congested, /* The async (write) queue is getting full */ BDI_async_congested, /* The async (write) queue is getting full */
BDI_sync_congested, /* The sync queue is getting full */ BDI_sync_congested, /* The sync queue is getting full */
BDI_registered, /* bdi_register() was done */
BDI_unused, /* Available bits start here */ BDI_unused, /* Available bits start here */
}; };
...@@ -39,7 +43,22 @@ enum bdi_stat_item { ...@@ -39,7 +43,22 @@ enum bdi_stat_item {
#define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids))) #define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids)))
struct bdi_writeback {
struct list_head list; /* hangs off the bdi */
struct backing_dev_info *bdi; /* our parent bdi */
unsigned int nr;
unsigned long last_old_flush; /* last old data flush */
struct task_struct *task; /* writeback task */
struct list_head b_dirty; /* dirty inodes */
struct list_head b_io; /* parked for writeback */
struct list_head b_more_io; /* parked for more writeback */
};
struct backing_dev_info { struct backing_dev_info {
struct list_head bdi_list;
unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */ unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */
unsigned long state; /* Always use atomic bitops on this */ unsigned long state; /* Always use atomic bitops on this */
unsigned int capabilities; /* Device capabilities */ unsigned int capabilities; /* Device capabilities */
...@@ -48,6 +67,8 @@ struct backing_dev_info { ...@@ -48,6 +67,8 @@ struct backing_dev_info {
void (*unplug_io_fn)(struct backing_dev_info *, struct page *); void (*unplug_io_fn)(struct backing_dev_info *, struct page *);
void *unplug_io_data; void *unplug_io_data;
char *name;
struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS]; struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];
struct prop_local_percpu completions; struct prop_local_percpu completions;
...@@ -56,6 +77,14 @@ struct backing_dev_info { ...@@ -56,6 +77,14 @@ struct backing_dev_info {
unsigned int min_ratio; unsigned int min_ratio;
unsigned int max_ratio, max_prop_frac; unsigned int max_ratio, max_prop_frac;
struct bdi_writeback wb; /* default writeback info for this bdi */
spinlock_t wb_lock; /* protects update side of wb_list */
struct list_head wb_list; /* the flusher threads hanging off this bdi */
unsigned long wb_mask; /* bitmask of registered tasks */
unsigned int wb_cnt; /* number of registered tasks */
struct list_head work_list;
struct device *dev; struct device *dev;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -71,6 +100,19 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent, ...@@ -71,6 +100,19 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent,
const char *fmt, ...); const char *fmt, ...);
int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev);
void bdi_unregister(struct backing_dev_info *bdi); void bdi_unregister(struct backing_dev_info *bdi);
void bdi_start_writeback(struct writeback_control *wbc);
int bdi_writeback_task(struct bdi_writeback *wb);
int bdi_has_dirty_io(struct backing_dev_info *bdi);
extern spinlock_t bdi_lock;
extern struct list_head bdi_list;
static inline int wb_has_dirty_io(struct bdi_writeback *wb)
{
return !list_empty(&wb->b_dirty) ||
!list_empty(&wb->b_io) ||
!list_empty(&wb->b_more_io);
}
static inline void __add_bdi_stat(struct backing_dev_info *bdi, static inline void __add_bdi_stat(struct backing_dev_info *bdi,
enum bdi_stat_item item, s64 amount) enum bdi_stat_item item, s64 amount)
...@@ -261,6 +303,11 @@ static inline bool bdi_cap_swap_backed(struct backing_dev_info *bdi) ...@@ -261,6 +303,11 @@ static inline bool bdi_cap_swap_backed(struct backing_dev_info *bdi)
return bdi->capabilities & BDI_CAP_SWAP_BACKED; return bdi->capabilities & BDI_CAP_SWAP_BACKED;
} }
static inline bool bdi_cap_flush_forker(struct backing_dev_info *bdi)
{
return bdi == &default_backing_dev_info;
}
static inline bool mapping_cap_writeback_dirty(struct address_space *mapping) static inline bool mapping_cap_writeback_dirty(struct address_space *mapping)
{ {
return bdi_cap_writeback_dirty(mapping->backing_dev_info); return bdi_cap_writeback_dirty(mapping->backing_dev_info);
...@@ -276,4 +323,10 @@ static inline bool mapping_cap_swap_backed(struct address_space *mapping) ...@@ -276,4 +323,10 @@ static inline bool mapping_cap_swap_backed(struct address_space *mapping)
return bdi_cap_swap_backed(mapping->backing_dev_info); return bdi_cap_swap_backed(mapping->backing_dev_info);
} }
static inline int bdi_sched_wait(void *word)
{
schedule();
return 0;
}
#endif /* _LINUX_BACKING_DEV_H */ #endif /* _LINUX_BACKING_DEV_H */
...@@ -715,7 +715,7 @@ struct posix_acl; ...@@ -715,7 +715,7 @@ struct posix_acl;
struct inode { struct inode {
struct hlist_node i_hash; struct hlist_node i_hash;
struct list_head i_list; struct list_head i_list; /* backing dev IO list */
struct list_head i_sb_list; struct list_head i_sb_list;
struct list_head i_dentry; struct list_head i_dentry;
unsigned long i_ino; unsigned long i_ino;
...@@ -1336,9 +1336,6 @@ struct super_block { ...@@ -1336,9 +1336,6 @@ struct super_block {
struct xattr_handler **s_xattr; struct xattr_handler **s_xattr;
struct list_head s_inodes; /* all inodes */ struct list_head s_inodes; /* all inodes */
struct list_head s_dirty; /* dirty inodes */
struct list_head s_io; /* parked for writeback */
struct list_head s_more_io; /* parked for more writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files; struct list_head s_files;
/* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
...@@ -1789,6 +1786,7 @@ extern int get_sb_pseudo(struct file_system_type *, char *, ...@@ -1789,6 +1786,7 @@ extern int get_sb_pseudo(struct file_system_type *, char *,
struct vfsmount *mnt); struct vfsmount *mnt);
extern void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb); extern void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb);
int __put_super_and_need_restart(struct super_block *sb); int __put_super_and_need_restart(struct super_block *sb);
void put_super(struct super_block *sb);
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */ /* Alas, no aliases. Too much hassle with bringing module.h everywhere */
#define fops_get(fops) \ #define fops_get(fops) \
...@@ -2071,8 +2069,6 @@ static inline void invalidate_remote_inode(struct inode *inode) ...@@ -2071,8 +2069,6 @@ static inline void invalidate_remote_inode(struct inode *inode)
extern int invalidate_inode_pages2(struct address_space *mapping); extern int invalidate_inode_pages2(struct address_space *mapping);
extern int invalidate_inode_pages2_range(struct address_space *mapping, extern int invalidate_inode_pages2_range(struct address_space *mapping,
pgoff_t start, pgoff_t end); pgoff_t start, pgoff_t end);
extern void generic_sync_sb_inodes(struct super_block *sb,
struct writeback_control *wbc);
extern int write_inode_now(struct inode *, int); extern int write_inode_now(struct inode *, int);
extern int filemap_fdatawrite(struct address_space *); extern int filemap_fdatawrite(struct address_space *);
extern int filemap_flush(struct address_space *); extern int filemap_flush(struct address_space *);
...@@ -2187,7 +2183,6 @@ extern int bdev_read_only(struct block_device *); ...@@ -2187,7 +2183,6 @@ extern int bdev_read_only(struct block_device *);
extern int set_blocksize(struct block_device *, int); extern int set_blocksize(struct block_device *, int);
extern int sb_set_blocksize(struct super_block *, int); extern int sb_set_blocksize(struct super_block *, int);
extern int sb_min_blocksize(struct super_block *, int); extern int sb_min_blocksize(struct super_block *, int);
extern int sb_has_dirty_inodes(struct super_block *);
extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int generic_file_mmap(struct file *, struct vm_area_struct *);
extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
......
...@@ -13,17 +13,6 @@ extern spinlock_t inode_lock; ...@@ -13,17 +13,6 @@ extern spinlock_t inode_lock;
extern struct list_head inode_in_use; extern struct list_head inode_in_use;
extern struct list_head inode_unused; extern struct list_head inode_unused;
/*
* Yes, writeback.h requires sched.h
* No, sched.h is not included from here.
*/
static inline int task_is_pdflush(struct task_struct *task)
{
return task->flags & PF_FLUSHER;
}
#define current_is_pdflush() task_is_pdflush(current)
/* /*
* fs/fs-writeback.c * fs/fs-writeback.c
*/ */
...@@ -40,6 +29,8 @@ enum writeback_sync_modes { ...@@ -40,6 +29,8 @@ enum writeback_sync_modes {
struct writeback_control { struct writeback_control {
struct backing_dev_info *bdi; /* If !NULL, only write back this struct backing_dev_info *bdi; /* If !NULL, only write back this
queue */ queue */
struct super_block *sb; /* if !NULL, only write inodes from
this super_block */
enum writeback_sync_modes sync_mode; enum writeback_sync_modes sync_mode;
unsigned long *older_than_this; /* If !NULL, only write back inodes unsigned long *older_than_this; /* If !NULL, only write back inodes
older than this */ older than this */
...@@ -76,9 +67,13 @@ struct writeback_control { ...@@ -76,9 +67,13 @@ struct writeback_control {
/* /*
* fs/fs-writeback.c * fs/fs-writeback.c
*/ */
void writeback_inodes(struct writeback_control *wbc); struct bdi_writeback;
int inode_wait(void *); int inode_wait(void *);
void sync_inodes_sb(struct super_block *, int wait); long writeback_inodes_sb(struct super_block *);
long sync_inodes_sb(struct super_block *);
void writeback_inodes_wbc(struct writeback_control *wbc);
long wb_do_writeback(struct bdi_writeback *wb, int force_wait);
void wakeup_flusher_threads(long nr_pages);
/* writeback.h requires fs.h; it, too, is not included from here. */ /* writeback.h requires fs.h; it, too, is not included from here. */
static inline void wait_on_inode(struct inode *inode) static inline void wait_on_inode(struct inode *inode)
...@@ -98,7 +93,6 @@ static inline void inode_sync_wait(struct inode *inode) ...@@ -98,7 +93,6 @@ static inline void inode_sync_wait(struct inode *inode)
/* /*
* mm/page-writeback.c * mm/page-writeback.c
*/ */
int wakeup_pdflush(long nr_pages);
void laptop_io_completion(void); void laptop_io_completion(void);
void laptop_sync_completion(void); void laptop_sync_completion(void);
void throttle_vm_writeout(gfp_t gfp_mask); void throttle_vm_writeout(gfp_t gfp_mask);
...@@ -150,7 +144,6 @@ balance_dirty_pages_ratelimited(struct address_space *mapping) ...@@ -150,7 +144,6 @@ balance_dirty_pages_ratelimited(struct address_space *mapping)
typedef int (*writepage_t)(struct page *page, struct writeback_control *wbc, typedef int (*writepage_t)(struct page *page, struct writeback_control *wbc,
void *data); void *data);
int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0);
int generic_writepages(struct address_space *mapping, int generic_writepages(struct address_space *mapping,
struct writeback_control *wbc); struct writeback_control *wbc);
int write_cache_pages(struct address_space *mapping, int write_cache_pages(struct address_space *mapping,
......
...@@ -600,6 +600,7 @@ static struct inode_operations cgroup_dir_inode_operations; ...@@ -600,6 +600,7 @@ static struct inode_operations cgroup_dir_inode_operations;
static struct file_operations proc_cgroupstats_operations; static struct file_operations proc_cgroupstats_operations;
static struct backing_dev_info cgroup_backing_dev_info = { static struct backing_dev_info cgroup_backing_dev_info = {
.name = "cgroup",
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
}; };
......
...@@ -8,7 +8,7 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ ...@@ -8,7 +8,7 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \
vmalloc.o vmalloc.o
obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \
maccess.o page_alloc.o page-writeback.o pdflush.o \ maccess.o page_alloc.o page-writeback.o \
readahead.o swap.o truncate.o vmscan.o shmem.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \
prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \
page_isolation.o mm_init.o $(mmu-y) page_isolation.o mm_init.o $(mmu-y)
......
This diff is collapsed.
...@@ -35,15 +35,6 @@ ...@@ -35,15 +35,6 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/pagevec.h> #include <linux/pagevec.h>
/*
* The maximum number of pages to writeout in a single bdflush/kupdate
* operation. We do this so we don't hold I_SYNC against an inode for
* enormous amounts of time, which would block a userspace task which has
* been forced to throttle against that inode. Also, the code reevaluates
* the dirty each time it has written this many pages.
*/
#define MAX_WRITEBACK_PAGES 1024
/* /*
* After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited * After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited
* will look to see if it needs to force writeback or throttling. * will look to see if it needs to force writeback or throttling.
...@@ -117,8 +108,6 @@ EXPORT_SYMBOL(laptop_mode); ...@@ -117,8 +108,6 @@ EXPORT_SYMBOL(laptop_mode);
/* End of sysctl-exported parameters */ /* End of sysctl-exported parameters */
static void background_writeout(unsigned long _min_pages);
/* /*
* Scale the writeback cache size proportional to the relative writeout speeds. * Scale the writeback cache size proportional to the relative writeout speeds.
* *
...@@ -320,15 +309,13 @@ static void task_dirty_limit(struct task_struct *tsk, unsigned long *pdirty) ...@@ -320,15 +309,13 @@ static void task_dirty_limit(struct task_struct *tsk, unsigned long *pdirty)
/* /*
* *
*/ */
static DEFINE_SPINLOCK(bdi_lock);
static unsigned int bdi_min_ratio; static unsigned int bdi_min_ratio;
int bdi_set_min_ratio(struct backing_dev_info *bdi, unsigned int min_ratio) int bdi_set_min_ratio(struct backing_dev_info *bdi, unsigned int min_ratio)
{ {
int ret = 0; int ret = 0;
unsigned long flags;
spin_lock_irqsave(&bdi_lock, flags); spin_lock(&bdi_lock);
if (min_ratio > bdi->max_ratio) { if (min_ratio > bdi->max_ratio) {
ret = -EINVAL; ret = -EINVAL;
} else { } else {
...@@ -340,27 +327,26 @@ int bdi_set_min_ratio(struct backing_dev_info *bdi, unsigned int min_ratio) ...@@ -340,27 +327,26 @@ int bdi_set_min_ratio(struct backing_dev_info *bdi, unsigned int min_ratio)
ret = -EINVAL; ret = -EINVAL;
} }
} }
spin_unlock_irqrestore(&bdi_lock, flags); spin_unlock(&bdi_lock);
return ret; return ret;
} }
int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned max_ratio) int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned max_ratio)
{ {
unsigned long flags;
int ret = 0; int ret = 0;
if (max_ratio > 100) if (max_ratio > 100)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&bdi_lock, flags); spin_lock(&bdi_lock);
if (bdi->min_ratio > max_ratio) { if (bdi->min_ratio > max_ratio) {
ret = -EINVAL; ret = -EINVAL;
} else { } else {
bdi->max_ratio = max_ratio; bdi->max_ratio = max_ratio;
bdi->max_prop_frac = (PROP_FRAC_BASE * max_ratio) / 100; bdi->max_prop_frac = (PROP_FRAC_BASE * max_ratio) / 100;
} }
spin_unlock_irqrestore(&bdi_lock, flags); spin_unlock(&bdi_lock);
return ret; return ret;
} }
...@@ -546,7 +532,7 @@ static void balance_dirty_pages(struct address_space *mapping) ...@@ -546,7 +532,7 @@ static void balance_dirty_pages(struct address_space *mapping)
* up. * up.
*/ */
if (bdi_nr_reclaimable > bdi_thresh) { if (bdi_nr_reclaimable > bdi_thresh) {
writeback_inodes(&wbc); writeback_inodes_wbc(&wbc);
pages_written += write_chunk - wbc.nr_to_write; pages_written += write_chunk - wbc.nr_to_write;
get_dirty_limits(&background_thresh, &dirty_thresh, get_dirty_limits(&background_thresh, &dirty_thresh,
&bdi_thresh, bdi); &bdi_thresh, bdi);
...@@ -575,7 +561,7 @@ static void balance_dirty_pages(struct address_space *mapping) ...@@ -575,7 +561,7 @@ static void balance_dirty_pages(struct address_space *mapping)
if (pages_written >= write_chunk) if (pages_written >= write_chunk)
break; /* We've done our duty */ break; /* We've done our duty */
congestion_wait(BLK_RW_ASYNC, HZ/10); schedule_timeout(1);
} }
if (bdi_nr_reclaimable + bdi_nr_writeback < bdi_thresh && if (bdi_nr_reclaimable + bdi_nr_writeback < bdi_thresh &&
...@@ -594,10 +580,18 @@ static void balance_dirty_pages(struct address_space *mapping) ...@@ -594,10 +580,18 @@ static void balance_dirty_pages(struct address_space *mapping)
* background_thresh, to keep the amount of dirty memory low. * background_thresh, to keep the amount of dirty memory low.
*/ */
if ((laptop_mode && pages_written) || if ((laptop_mode && pages_written) ||
(!laptop_mode && (global_page_state(NR_FILE_DIRTY) (!laptop_mode && ((nr_writeback = global_page_state(NR_FILE_DIRTY)
+ global_page_state(NR_UNSTABLE_NFS) + global_page_state(NR_UNSTABLE_NFS))
> background_thresh))) > background_thresh))) {
pdflush_operation(background_writeout, 0); struct writeback_control wbc = {
.bdi = bdi,
.sync_mode = WB_SYNC_NONE,
.nr_to_write = nr_writeback,
};
bdi_start_writeback(&wbc);
}
} }
void set_page_dirty_balance(struct page *page, int page_mkwrite) void set_page_dirty_balance(struct page *page, int page_mkwrite)
...@@ -681,124 +675,10 @@ void throttle_vm_writeout(gfp_t gfp_mask) ...@@ -681,124 +675,10 @@ void throttle_vm_writeout(gfp_t gfp_mask)
} }
} }
/*
* writeback at least _min_pages, and keep writing until the amount of dirty
* memory is less than the background threshold, or until we're all clean.
*/
static void background_writeout(unsigned long _min_pages)
{
long min_pages = _min_pages;
struct writeback_control wbc = {
.bdi = NULL,
.sync_mode = WB_SYNC_NONE,
.older_than_this = NULL,
.nr_to_write = 0,
.nonblocking = 1,
.range_cyclic = 1,
};
for ( ; ; ) {
unsigned long background_thresh;
unsigned long dirty_thresh;
get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL);
if (global_page_state(NR_FILE_DIRTY) +
global_page_state(NR_UNSTABLE_NFS) < background_thresh
&& min_pages <= 0)
break;
wbc.more_io = 0;
wbc.encountered_congestion = 0;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
wbc.pages_skipped = 0;
writeback_inodes(&wbc);
min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
if (wbc.nr_to_write > 0 || wbc.pages_skipped > 0) {
/* Wrote less than expected */
if (wbc.encountered_congestion || wbc.more_io)
congestion_wait(BLK_RW_ASYNC, HZ/10);
else
break;
}
}
}
/*
* Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back
* the whole world. Returns 0 if a pdflush thread was dispatched. Returns
* -1 if all pdflush threads were busy.
*/
int wakeup_pdflush(long nr_pages)
{
if (nr_pages == 0)
nr_pages = global_page_state(NR_FILE_DIRTY) +
global_page_state(NR_UNSTABLE_NFS);
return pdflush_operation(background_writeout, nr_pages);
}
static void wb_timer_fn(unsigned long unused);
static void laptop_timer_fn(unsigned long unused); static void laptop_timer_fn(unsigned long unused);
static DEFINE_TIMER(wb_timer, wb_timer_fn, 0, 0);
static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0); static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0);
/*
* Periodic writeback of "old" data.
*
* Define "old": the first time one of an inode's pages is dirtied, we mark the
* dirtying-time in the inode's address_space. So this periodic writeback code
* just walks the superblock inode list, writing back any inodes which are
* older than a specific point in time.
*
* Try to run once per dirty_writeback_interval. But if a writeback event
* takes longer than a dirty_writeback_interval interval, then leave a
* one-second gap.
*
* older_than_this takes precedence over nr_to_write. So we'll only write back
* all dirty pages if they are all attached to "old" mappings.
*/
static void wb_kupdate(unsigned long arg)
{
unsigned long oldest_jif;
unsigned long start_jif;
unsigned long next_jif;
long nr_to_write;
struct writeback_control wbc = {
.bdi = NULL,
.sync_mode = WB_SYNC_NONE,
.older_than_this = &oldest_jif,
.nr_to_write = 0,
.nonblocking = 1,
.for_kupdate = 1,
.range_cyclic = 1,
};
sync_supers();
oldest_jif = jiffies - msecs_to_jiffies(dirty_expire_interval * 10);
start_jif = jiffies;
next_jif = start_jif + msecs_to_jiffies(dirty_writeback_interval * 10);
nr_to_write = global_page_state(NR_FILE_DIRTY) +
global_page_state(NR_UNSTABLE_NFS) +
(inodes_stat.nr_inodes - inodes_stat.nr_unused);
while (nr_to_write > 0) {
wbc.more_io = 0;
wbc.encountered_congestion = 0;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
writeback_inodes(&wbc);
if (wbc.nr_to_write > 0) {
if (wbc.encountered_congestion || wbc.more_io)
congestion_wait(BLK_RW_ASYNC, HZ/10);
else
break; /* All the old data is written */
}
nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
}
if (time_before(next_jif, jiffies + HZ))
next_jif = jiffies + HZ;
if (dirty_writeback_interval)
mod_timer(&wb_timer, next_jif);
}
/* /*
* sysctl handler for /proc/sys/vm/dirty_writeback_centisecs * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
*/ */
...@@ -806,28 +686,24 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write, ...@@ -806,28 +686,24 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
struct file *file, void __user *buffer, size_t *length, loff_t *ppos) struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
{ {
proc_dointvec(table, write, file, buffer, length, ppos); proc_dointvec(table, write, file, buffer, length, ppos);
if (dirty_writeback_interval)
mod_timer(&wb_timer, jiffies +
msecs_to_jiffies(dirty_writeback_interval * 10));
else
del_timer(&wb_timer);
return 0; return 0;
} }
static void wb_timer_fn(unsigned long unused) static void do_laptop_sync(struct work_struct *work)
{
if (pdflush_operation(wb_kupdate, 0) < 0)
mod_timer(&wb_timer, jiffies + HZ); /* delay 1 second */
}
static void laptop_flush(unsigned long unused)
{ {
sys_sync(); wakeup_flusher_threads(0);
kfree(work);
} }
static void laptop_timer_fn(unsigned long unused) static void laptop_timer_fn(unsigned long unused)
{ {
pdflush_operation(laptop_flush, 0); struct work_struct *work;
work = kmalloc(sizeof(*work), GFP_ATOMIC);
if (work) {
INIT_WORK(work, do_laptop_sync);
schedule_work(work);
}
} }
/* /*
...@@ -910,8 +786,6 @@ void __init page_writeback_init(void) ...@@ -910,8 +786,6 @@ void __init page_writeback_init(void)
{ {
int shift; int shift;
mod_timer(&wb_timer,
jiffies + msecs_to_jiffies(dirty_writeback_interval * 10));
writeback_set_ratelimit(); writeback_set_ratelimit();
register_cpu_notifier(&ratelimit_nb); register_cpu_notifier(&ratelimit_nb);
......
/*
* mm/pdflush.c - worker threads for writing back filesystem data
*
* Copyright (C) 2002, Linus Torvalds.
*
* 09Apr2002 Andrew Morton
* Initial version
* 29Feb2004 kaos@sgi.com
* Move worker thread creation to kthread to avoid chewing
* up stack space with nested calls to kernel_thread.
*/
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/signal.h>
#include <linux/spinlock.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> /* Needed by writeback.h */
#include <linux/writeback.h> /* Prototypes pdflush_operation() */
#include <linux/kthread.h>
#include <linux/cpuset.h>
#include <linux/freezer.h>
/*
* Minimum and maximum number of pdflush instances
*/
#define MIN_PDFLUSH_THREADS 2
#define MAX_PDFLUSH_THREADS 8
static void start_one_pdflush_thread(void);
/*
* The pdflush threads are worker threads for writing back dirty data.
* Ideally, we'd like one thread per active disk spindle. But the disk
* topology is very hard to divine at this level. Instead, we take
* care in various places to prevent more than one pdflush thread from
* performing writeback against a single filesystem. pdflush threads
* have the PF_FLUSHER flag set in current->flags to aid in this.
*/
/*
* All the pdflush threads. Protected by pdflush_lock
*/
static LIST_HEAD(pdflush_list);
static DEFINE_SPINLOCK(pdflush_lock);
/*
* The count of currently-running pdflush threads. Protected
* by pdflush_lock.
*
* Readable by sysctl, but not writable. Published to userspace at
* /proc/sys/vm/nr_pdflush_threads.
*/
int nr_pdflush_threads = 0;
/*
* The time at which the pdflush thread pool last went empty
*/
static unsigned long last_empty_jifs;
/*
* The pdflush thread.
*
* Thread pool management algorithm:
*
* - The minimum and maximum number of pdflush instances are bound
* by MIN_PDFLUSH_THREADS and MAX_PDFLUSH_THREADS.
*
* - If there have been no idle pdflush instances for 1 second, create
* a new one.
*
* - If the least-recently-went-to-sleep pdflush thread has been asleep
* for more than one second, terminate a thread.
*/
/*
* A structure for passing work to a pdflush thread. Also for passing
* state information between pdflush threads. Protected by pdflush_lock.
*/
struct pdflush_work {
struct task_struct *who; /* The thread */
void (*fn)(unsigned long); /* A callback function */
unsigned long arg0; /* An argument to the callback */
struct list_head list; /* On pdflush_list, when idle */
unsigned long when_i_went_to_sleep;
};
static int __pdflush(struct pdflush_work *my_work)
{
current->flags |= PF_FLUSHER | PF_SWAPWRITE;
set_freezable();
my_work->fn = NULL;
my_work->who = current;
INIT_LIST_HEAD(&my_work->list);
spin_lock_irq(&pdflush_lock);
for ( ; ; ) {
struct pdflush_work *pdf;
set_current_state(TASK_INTERRUPTIBLE);
list_move(&my_work->list, &pdflush_list);
my_work->when_i_went_to_sleep = jiffies;
spin_unlock_irq(&pdflush_lock);
schedule();
try_to_freeze();
spin_lock_irq(&pdflush_lock);
if (!list_empty(&my_work->list)) {
/*
* Someone woke us up, but without removing our control
* structure from the global list. swsusp will do this
* in try_to_freeze()->refrigerator(). Handle it.
*/
my_work->fn = NULL;
continue;
}
if (my_work->fn == NULL) {
printk("pdflush: bogus wakeup\n");
continue;
}
spin_unlock_irq(&pdflush_lock);
(*my_work->fn)(my_work->arg0);
spin_lock_irq(&pdflush_lock);
/*
* Thread creation: For how long have there been zero
* available threads?
*
* To throttle creation, we reset last_empty_jifs.
*/
if (time_after(jiffies, last_empty_jifs + 1 * HZ)) {
if (list_empty(&pdflush_list)) {
if (nr_pdflush_threads < MAX_PDFLUSH_THREADS) {
last_empty_jifs = jiffies;
nr_pdflush_threads++;
spin_unlock_irq(&pdflush_lock);
start_one_pdflush_thread();
spin_lock_irq(&pdflush_lock);
}
}
}
my_work->fn = NULL;
/*
* Thread destruction: For how long has the sleepiest
* thread slept?
*/
if (list_empty(&pdflush_list))
continue;
if (nr_pdflush_threads <= MIN_PDFLUSH_THREADS)
continue;
pdf = list_entry(pdflush_list.prev, struct pdflush_work, list);
if (time_after(jiffies, pdf->when_i_went_to_sleep + 1 * HZ)) {
/* Limit exit rate */
pdf->when_i_went_to_sleep = jiffies;
break; /* exeunt */
}
}
nr_pdflush_threads--;
spin_unlock_irq(&pdflush_lock);
return 0;
}
/*
* Of course, my_work wants to be just a local in __pdflush(). It is
* separated out in this manner to hopefully prevent the compiler from
* performing unfortunate optimisations against the auto variables. Because
* these are visible to other tasks and CPUs. (No problem has actually
* been observed. This is just paranoia).
*/
static int pdflush(void *dummy)
{
struct pdflush_work my_work;
cpumask_var_t cpus_allowed;
/*
* Since the caller doesn't even check kthread_run() worked, let's not
* freak out too much if this fails.
*/
if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) {
printk(KERN_WARNING "pdflush failed to allocate cpumask\n");
return 0;
}
/*
* pdflush can spend a lot of time doing encryption via dm-crypt. We
* don't want to do that at keventd's priority.
*/
set_user_nice(current, 0);
/*
* Some configs put our parent kthread in a limited cpuset,
* which kthread() overrides, forcing cpus_allowed == cpu_all_mask.
* Our needs are more modest - cut back to our cpusets cpus_allowed.
* This is needed as pdflush's are dynamically created and destroyed.
* The boottime pdflush's are easily placed w/o these 2 lines.
*/
cpuset_cpus_allowed(current, cpus_allowed);
set_cpus_allowed_ptr(current, cpus_allowed);
free_cpumask_var(cpus_allowed);
return __pdflush(&my_work);
}
/*
* Attempt to wake up a pdflush thread, and get it to do some work for you.
* Returns zero if it indeed managed to find a worker thread, and passed your
* payload to it.
*/
int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
{
unsigned long flags;
int ret = 0;
BUG_ON(fn == NULL); /* Hard to diagnose if it's deferred */
spin_lock_irqsave(&pdflush_lock, flags);
if (list_empty(&pdflush_list)) {
ret = -1;
} else {
struct pdflush_work *pdf;
pdf = list_entry(pdflush_list.next, struct pdflush_work, list);
list_del_init(&pdf->list);
if (list_empty(&pdflush_list))
last_empty_jifs = jiffies;
pdf->fn = fn;
pdf->arg0 = arg0;
wake_up_process(pdf->who);
}
spin_unlock_irqrestore(&pdflush_lock, flags);
return ret;
}
static void start_one_pdflush_thread(void)
{
struct task_struct *k;
k = kthread_run(pdflush, NULL, "pdflush");
if (unlikely(IS_ERR(k))) {
spin_lock_irq(&pdflush_lock);
nr_pdflush_threads--;
spin_unlock_irq(&pdflush_lock);
}
}
static int __init pdflush_init(void)
{
int i;
/*
* Pre-set nr_pdflush_threads... If we fail to create,
* the count will be decremented.
*/
nr_pdflush_threads = MIN_PDFLUSH_THREADS;
for (i = 0; i < MIN_PDFLUSH_THREADS; i++)
start_one_pdflush_thread();
return 0;
}
module_init(pdflush_init);
...@@ -34,6 +34,7 @@ static const struct address_space_operations swap_aops = { ...@@ -34,6 +34,7 @@ static const struct address_space_operations swap_aops = {
}; };
static struct backing_dev_info swap_backing_dev_info = { static struct backing_dev_info swap_backing_dev_info = {
.name = "swap",
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED, .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED,
.unplug_io_fn = swap_unplug_io_fn, .unplug_io_fn = swap_unplug_io_fn,
}; };
......
...@@ -1720,7 +1720,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, ...@@ -1720,7 +1720,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
*/ */
if (total_scanned > sc->swap_cluster_max + if (total_scanned > sc->swap_cluster_max +
sc->swap_cluster_max / 2) { sc->swap_cluster_max / 2) {
wakeup_pdflush(laptop_mode ? 0 : total_scanned); wakeup_flusher_threads(laptop_mode ? 0 : total_scanned);
sc->may_writepage = 1; sc->may_writepage = 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment