[lkml]   [2008]   [Oct]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
Messages in this thread
    Patch in this message
    Subject[patch 0/7] cpuset writeback throttling

    This is the revised cpuset writeback throttling patchset posted to LKML
    on Tuesday, October 27.

    The comments from Peter Zijlstra have been addressed. His concurrent
    page cache patchset is not currently in -mm, so we can still serialize
    updating a struct address_space's dirty_nodes on its tree_lock. When his
    patchset is merged, the patch at the end of this message can be used to
    introduce the necessary synchronization.

    This patchset applies nicely to 2.6.28-rc2-mm1 with the exception of the
    first patch due to the alloc_inode() refactoring to inode_init_always() in
    e9110864c440736beb484c2c74dedc307168b14e from linux-next and additions to
    include/linux/cpuset.h from
    oom-print-triggering-tasks-cpuset-and-mems-allowed.patch (oops :).

    Please consider this for inclusion in the -mm tree.

    A simple way of testing this change is to create a large file that exceeds
    the amount of memory allocated to a specific cpuset. Then, mmap and
    modify the large file (such as in the following program) while running a
    latency sensitive task in a disjoint cpuset. Notice the writeout
    throttling that doesn't interfere with the latency sensitive task.

    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/mman.h>
    #include <fcntl.h>

    int main(int argc, char **argv)
    void *addr;
    unsigned long length;
    unsigned long i;
    int fd;

    if (argc != 3) {
    fprintf(stderr, "usage: %s <filename> <length>\n",

    fd = open(argv[1], O_RDWR, 0644);
    if (fd < 0) {
    fprintf(stderr, "Cannot open file %s\n", argv[1]);

    length = strtoul(argv[2], NULL, 0);
    if (!length) {
    fprintf(stderr, "Invalid length %s\n", argv[2]);

    addr = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
    if (addr == MAP_FAILED) {
    fprintf(stderr, "mmap() failed\n");

    for (;;) {
    for (i = 0; i < length; i++)
    (*(char *)(addr + i))++;
    msync(addr, length, MS_SYNC);
    return 0;

    The following patch can be applied once the struct address_space's
    tree_lock is removed to protect the attachment of mapping->dirty_nodes.
    diff --git a/fs/inode.c b/fs/inode.c
    --- a/fs/inode.c
    +++ b/fs/inode.c
    @@ -223,6 +223,9 @@ void inode_init_once(struct inode *inode)
    + spin_lock_init(&inode->i_data.dirty_nodes_lock);

    diff --git a/include/linux/fs.h b/include/linux/fs.h
    --- a/include/linux/fs.h
    +++ b/include/linux/fs.h
    @@ -554,6 +554,7 @@ struct address_space {
    nodemask_t dirty_nodes; /* nodes with dirty pages */
    nodemask_t *dirty_nodes; /* pointer to mask, if dirty */
    + spinlock_t dirty_nodes_lock; /* protects the above */
    } __attribute__((aligned(sizeof(long))));
    diff --git a/kernel/cpuset.c b/kernel/cpuset.c
    --- a/kernel/cpuset.c
    +++ b/kernel/cpuset.c
    @@ -2413,25 +2413,27 @@ EXPORT_SYMBOL_GPL(cpuset_mem_spread_node);
    * Special functions for NUMA systems with a large number of nodes. The
    - * nodemask is pointed to from the address_space structure. The attachment of
    - * the dirty_nodes nodemask is protected by the tree_lock. The nodemask is
    - * freed only when the inode is cleared (and therefore unused, thus no locking
    - * is necessary).
    + * nodemask is pointed to from the address_space structure.
    void cpuset_update_dirty_nodes(struct address_space *mapping,
    struct page *page)
    - nodemask_t *nodes = mapping->dirty_nodes;
    + nodemask_t *nodes;
    int node = page_to_nid(page);

    + spin_lock_irq(&mapping->dirty_nodes_lock);
    + nodes = mapping->dirty_nodes;
    if (!nodes) {
    nodes = kmalloc(sizeof(nodemask_t), GFP_ATOMIC);
    - if (!nodes)
    + if (!nodes) {
    + spin_unlock_irq(&mapping->dirty_nodes_lock);
    + }

    *nodes = NODE_MASK_NONE;
    mapping->dirty_nodes = nodes;
    + spin_unlock_irq(&mapping->dirty_nodes_lock);
    node_set(node, *nodes);

    @@ -2446,8 +2448,8 @@ void cpuset_clear_dirty_nodes(struct address_space *mapping)

    - * Called without tree_lock. The nodemask is only freed when the inode is
    - * cleared and therefore this is safe.
    + * The nodemask is only freed when the inode is cleared and therefore this
    + * requires no locking.
    int cpuset_intersects_dirty_nodes(struct address_space *mapping,
    nodemask_t *mask)

     \ /
      Last update: 2008-10-30 20:29    [W:0.028 / U:4.288 seconds]
    ©2003-2017 Jasper Spaans. hosted at Digital OceanAdvertise on this site