lkml.org 
[lkml]   [2003]   [May]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] laptop mode, #2
    Hi,

    Made a few tweaks and adjustments:

    - If block_dump is set, also dump who is marking a page/buffer as dirty.
    akpm recommended this.

    - Don't touch default bdflush parameters (see script)

    That's about it. I've gotten several mails who really like the patch and
    that it really adds a non-significant amount of extra battery time. I
    consider the patch final at this point.

    Patch is against 2.4.21-rc2 (ish)

    --
    Jens Axboe

    #!/bin/sh
    #
    # start of stop laptop mode, best run by a power management daemon when
    # ac gets connected/disconnected from a laptop
    #
    # FIXME: assumes HZ == 100

    # age time, in seconds. should be put into a sysconfig file
    MAX_AGE=600

    # kernel default dirty buffer age
    DEF_AGE=30
    DEF_UPDATE=5

    if [ ! -w /proc/sys/vm/laptop_mode ]; then
    echo "Kernel is not patched with laptop_mode patch"
    exit 1
    fi

    case "$1" in
    start)
    AGE=$((100*$MAX_AGE))
    echo -n "Starting laptop mode"
    echo "1" > /proc/sys/vm/laptop_mode
    echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
    echo "."
    ;;
    stop)
    U_AGE=$((100*$DEF_UPDATE))
    B_AGE=$((100*$DEF_AGE))
    echo -n "Stopping laptop mode"
    echo "0" > /proc/sys/vm/laptop_mode
    echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
    echo "."
    ;;
    *)
    echo "$0 {start|stop}"
    ;;

    esac

    exit 0
    # This is a BitKeeper generated patch for the following project:
    # Project Name: Linux kernel tree
    # This patch format is intended for GNU patch command version 2.5 or higher.
    # This patch includes the following deltas:
    # ChangeSet 1.1209 -> 1.1210
    # include/linux/sysctl.h 1.23 -> 1.24
    # drivers/block/ll_rw_blk.c 1.44 -> 1.45
    # kernel/sysctl.c 1.19 -> 1.20
    # include/linux/fs.h 1.74 -> 1.75
    # fs/jbd/transaction.c 1.13 -> 1.14
    # mm/filemap.c 1.76 -> 1.77
    # fs/buffer.c 1.82 -> 1.83
    #
    # The following is the BitKeeper ChangeSet Log
    # --------------------------------------------
    # 03/05/16 axboe@smithers.home.kernel.dk 1.1210
    # laptop mode #2
    # --------------------------------------------
    #
    diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
    --- a/drivers/block/ll_rw_blk.c Fri May 16 13:32:56 2003
    +++ b/drivers/block/ll_rw_blk.c Fri May 16 13:32:56 2003
    @@ -121,6 +121,10 @@
    unsigned long blk_max_low_pfn, blk_max_pfn;
    int blk_nohighio = 0;

    +int block_dump = 0;
    +
    +static struct timer_list writeback_timer;
    +
    static inline int get_max_sectors(kdev_t dev)
    {
    if (!max_sectors[MAJOR(dev)])
    @@ -1207,6 +1211,9 @@
    kstat.pgpgin += count;
    break;
    }
    +
    + if (block_dump)
    + printk("%s: %s block %lu/%u on %s\n", current->comm, rw == WRITE ? "WRITE" : "READ", bh->b_rsector, count, kdevname(bh->b_rdev));
    }

    /**
    @@ -1318,6 +1325,11 @@
    extern int stram_device_init (void);
    #endif

    +static void blk_writeback_timer(unsigned long data)
    +{
    + wakeup_bdflush();
    + wakeup_kupdate();
    +}

    /**
    * end_that_request_first - end I/O on one buffer.
    @@ -1373,10 +1385,18 @@
    return 0;
    }

    +extern int laptop_mode;
    +
    void end_that_request_last(struct request *req)
    {
    struct completion *waiting = req->waiting;

    + /*
    + * schedule the writeout of pending dirty data when the disk is idle
    + */
    + if (laptop_mode && req->cmd == READ)
    + mod_timer(&writeback_timer, jiffies + 5 * HZ);
    +
    req_finished_io(req);
    blkdev_release_request(req);
    if (waiting)
    @@ -1403,6 +1423,9 @@

    blk_max_low_pfn = max_low_pfn - 1;
    blk_max_pfn = max_pfn - 1;
    +
    + init_timer(&writeback_timer);
    + writeback_timer.function = blk_writeback_timer;

    #ifdef CONFIG_AMIGA_Z2RAM
    z2_init();
    diff -Nru a/fs/buffer.c b/fs/buffer.c
    --- a/fs/buffer.c Fri May 16 13:32:56 2003
    +++ b/fs/buffer.c Fri May 16 13:32:56 2003
    @@ -88,6 +88,13 @@
    static int osync_buffers_list(struct list_head *);
    static void __refile_buffer(struct buffer_head *);

    +/*
    + * A global sysctl-controlled flag which puts the machine into "laptop mode"
    + */
    +int laptop_mode;
    +
    +static DECLARE_WAIT_QUEUE_HEAD(kupdate_wait);
    +
    /* This is used by some architectures to estimate available memory. */
    atomic_t buffermem_pages = ATOMIC_INIT(0);

    @@ -995,7 +1002,7 @@
    dirty *= 100;
    dirty_limit = tot * bdf_prm.b_un.nfract_stop_bdflush;

    - if (dirty > dirty_limit)
    + if (!laptop_mode && dirty > dirty_limit)
    return 0;
    return 1;
    }
    @@ -1044,6 +1051,8 @@
    void mark_buffer_dirty(struct buffer_head *bh)
    {
    if (!atomic_set_buffer_dirty(bh)) {
    + if (block_dump)
    + printk("%s: dirtied buffer\n", current->comm);
    __mark_dirty(bh);
    balance_dirty();
    }
    @@ -1055,6 +1064,13 @@
    }
    EXPORT_SYMBOL(set_buffer_flushtime);

    +unsigned long get_buffer_flushtime(void)
    +{
    + return bdf_prm.b_un.age_buffer;
    +}
    +EXPORT_SYMBOL(get_buffer_flushtime);
    +
    +
    /*
    * A buffer may need to be moved from one buffer list to another
    * (e.g. in case it is not shared any more). Handle this.
    @@ -2815,6 +2831,12 @@
    wake_up_interruptible(&bdflush_wait);
    }

    +void wakeup_kupdate(void)
    +{
    + if (waitqueue_active(&kupdate_wait))
    + wake_up(&kupdate_wait);
    +}
    +
    /*
    * Here we attempt to write back old buffers. We also try to flush inodes
    * and supers as well, since this function is essentially "update", and
    @@ -2835,7 +2857,9 @@

    spin_lock(&lru_list_lock);
    bh = lru_list[BUF_DIRTY];
    - if (!bh || time_before(jiffies, bh->b_flushtime))
    + if (!bh)
    + break;
    + if (time_before(jiffies, bh->b_flushtime) && !laptop_mode)
    break;
    if (write_some_buffers(NODEV))
    continue;
    @@ -2983,6 +3007,10 @@
    complete((struct completion *)startup);

    for (;;) {
    + DECLARE_WAITQUEUE(wait, tsk);
    +
    + add_wait_queue(&kupdate_wait, &wait);
    +
    /* update interval */
    interval = bdf_prm.b_un.interval;
    if (interval) {
    @@ -2993,6 +3021,7 @@
    tsk->state = TASK_STOPPED;
    schedule(); /* wait for SIGCONT */
    }
    + remove_wait_queue(&kupdate_wait, &wait);
    /* check for sigstop */
    if (signal_pending(tsk)) {
    int stopped = 0;
    @@ -3010,6 +3039,8 @@
    printk(KERN_DEBUG "kupdate() activated...\n");
    #endif
    sync_old_buffers();
    + if (laptop_mode)
    + fsync_dev(NODEV);
    run_task_queue(&tq_disk);
    }
    }
    diff -Nru a/fs/jbd/transaction.c b/fs/jbd/transaction.c
    --- a/fs/jbd/transaction.c Fri May 16 13:32:56 2003
    +++ b/fs/jbd/transaction.c Fri May 16 13:32:56 2003
    @@ -56,7 +56,11 @@
    transaction->t_journal = journal;
    transaction->t_state = T_RUNNING;
    transaction->t_tid = journal->j_transaction_sequence++;
    - transaction->t_expires = jiffies + journal->j_commit_interval;
    + /*
    + * have to do it here, otherwise changed age_buffers since boot
    + * wont have any effect
    + */
    + transaction->t_expires = jiffies + get_buffer_flushtime();
    INIT_LIST_HEAD(&transaction->t_jcb);

    /* Set up the commit timer for the new transaction. */
    diff -Nru a/include/linux/fs.h b/include/linux/fs.h
    --- a/include/linux/fs.h Fri May 16 13:32:56 2003
    +++ b/include/linux/fs.h Fri May 16 13:32:56 2003
    @@ -1238,6 +1238,7 @@
    }

    extern void set_buffer_flushtime(struct buffer_head *);
    +extern unsigned long get_buffer_flushtime(void);
    extern void balance_dirty(void);
    extern int check_disk_change(kdev_t);
    extern int invalidate_inodes(struct super_block *);
    @@ -1427,8 +1428,10 @@
    return get_hash_table(sb->s_dev, block, sb->s_blocksize);
    }
    extern void wakeup_bdflush(void);
    +extern void wakeup_kupdate(void);
    extern void put_unused_buffer_head(struct buffer_head * bh);
    extern struct buffer_head * get_unused_buffer_head(int async);
    +extern int block_dump;

    extern int brw_page(int, struct page *, kdev_t, int [], int);

    diff -Nru a/include/linux/sysctl.h b/include/linux/sysctl.h
    --- a/include/linux/sysctl.h Fri May 16 13:32:56 2003
    +++ b/include/linux/sysctl.h Fri May 16 13:32:56 2003
    @@ -144,6 +144,8 @@
    VM_MAX_MAP_COUNT=11, /* int: Maximum number of active map areas */
    VM_MIN_READAHEAD=12, /* Min file readahead */
    VM_MAX_READAHEAD=13, /* Max file readahead */
    + VM_LAPTOP_MODE=14, /* vm laptop mode */
    + VM_BLOCK_DUMP=15, /* dump data read/write and dirtying */
    };


    diff -Nru a/kernel/sysctl.c b/kernel/sysctl.c
    --- a/kernel/sysctl.c Fri May 16 13:32:56 2003
    +++ b/kernel/sysctl.c Fri May 16 13:32:56 2003
    @@ -51,6 +51,8 @@
    extern int core_uses_pid;
    extern char core_pattern[];
    extern int cad_pid;
    +extern int laptop_mode;
    +extern int block_dump;

    /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
    static int maxolduid = 65535;
    @@ -280,6 +282,10 @@
    &vm_max_readahead,sizeof(int), 0644, NULL, &proc_dointvec},
    {VM_MAX_MAP_COUNT, "max_map_count",
    &max_map_count, sizeof(int), 0644, NULL, &proc_dointvec},
    + {VM_LAPTOP_MODE, "laptop_mode",
    + &laptop_mode, sizeof(int), 0644, NULL, &proc_dointvec},
    + {VM_BLOCK_DUMP, "block_dump",
    + &block_dump, sizeof(int), 0644, NULL, &proc_dointvec},
    {0}
    };

    diff -Nru a/mm/filemap.c b/mm/filemap.c
    --- a/mm/filemap.c Fri May 16 13:32:56 2003
    +++ b/mm/filemap.c Fri May 16 13:32:56 2003
    @@ -166,6 +166,8 @@

    if (mapping && mapping->host)
    mark_inode_dirty_pages(mapping->host);
    + if (block_dump)
    + printk("%s: dirtied page\n", current->comm);
    }
    }
    }
    \
     
     \ /
      Last update: 2005-03-22 13:35    [W:0.032 / U:0.916 seconds]
    ©2003-2017 Jasper Spaans. hosted at Digital OceanAdvertise on this site