diff -Nur sauberer_kernel/linux-3.1.4/drivers/scsi/scsi_sysfs.c unser_kernel/linux-3.1.4/drivers/scsi/scsi_sysfs.c --- sauberer_kernel/linux-3.1.4/drivers/scsi/scsi_sysfs.c 2011-11-28 23:48:14.000000000 +0100 +++ unser_kernel/linux-3.1.4/drivers/scsi/scsi_sysfs.c 2012-02-05 14:41:28.219258380 +0100 @@ -498,6 +498,7 @@ sdev_rd_attr (vendor, "%.8s\n"); sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); +sdev_rw_attr (break_even_period, "%lu\n"); /* * TODO: can we make these symlinks to the block layer ones? @@ -692,6 +693,7 @@ &dev_attr_iodone_cnt.attr, &dev_attr_ioerr_cnt.attr, &dev_attr_modalias.attr, + &dev_attr_break_even_period.attr, REF_EVT(media_change), NULL }; diff -Nur sauberer_kernel/linux-3.1.4/fs/fat/inode.c unser_kernel/linux-3.1.4/fs/fat/inode.c --- sauberer_kernel/linux-3.1.4/fs/fat/inode.c 2011-11-28 23:48:14.000000000 +0100 +++ unser_kernel/linux-3.1.4/fs/fat/inode.c 2012-02-05 14:43:49.263961832 +0100 @@ -27,7 +27,9 @@ #include #include #include +#include #include "fat.h" +#include #ifndef CONFIG_FAT_DEFAULT_IOCHARSET /* if user don't select VFAT, this is undefined. */ @@ -105,41 +107,337 @@ return 0; } +// HACK +// CODE of the assignment begins here +static int fat_get_block_read(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct block_device *hdd; + struct scsi_device *scsi_device; + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err; + + //printk(KERN_DEBUG "fat_get_block(): entered\n"); + + /* Setting the device this way does NOT work, + * i.e. the blocks are still written to the original + * device (the one that was mounted). */ + /* + printk(KERN_DEBUG "fat_get_block(): changing blkdev...\n"); + + cache_bdev = lookup_bdev("/dev/sdb1"); + blkdev_get(cache_bdev, FMODE_READ | FMODE_WRITE, 0); + bh_result->b_bdev = cache_bdev; + + printk(KERN_DEBUG "fat_get_block(): blkdev changed.\n"); + */ + + + err = __fat_get_block(inode, iblock, &max_blocks, bh_result, create); + if (err) + return err; + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + + //printk(KERN_ERR "fat_get_block(): blocknr=%llu\n", bh_result->b_blocknr); + + + /* Set device to HDD -> spins up on all reads. */ + hdd = lookup_bdev("/dev/sda1"); + blkdev_get(hdd, FMODE_READ | FMODE_WRITE, 0); + scsi_device = hdd->bd_disk->queue->queuedata; /* we must be sure it's actually an scsi device */ + + + // magic number warning + // 625... is the "end" of the ssd + + if (/*1 == 1 ||*/ scsi_device->power_mode == 1 /*SCSI_RUNNING*/ || bh_result->b_blocknr >= 62533296) { + bh_result->b_bdev = hdd; + } + + return 0; +} static int fat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) + struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; int err; + //printk(KERN_DEBUG "fat_get_block(): entered\n"); + + /* Setting the device this way does NOT work, + * i.e. the blocks are still written to the original + * device (the one that was mounted). */ + /* + printk(KERN_DEBUG "fat_get_block(): changing blkdev...\n"); + + cache_bdev = lookup_bdev("/dev/sdb1"); + blkdev_get(cache_bdev, FMODE_READ | FMODE_WRITE, 0); + bh_result->b_bdev = cache_bdev; + + printk(KERN_DEBUG "fat_get_block(): blkdev changed.\n"); + */ + + err = __fat_get_block(inode, iblock, &max_blocks, bh_result, create); if (err) return err; bh_result->b_size = max_blocks << sb->s_blocksize_bits; + + //printk(KERN_ERR "fat_get_block(): blocknr=%llu\n", bh_result->b_blocknr); return 0; } +// HACK END -static int fat_writepage(struct page *page, struct writeback_control *wbc) +static int fat_set_device_in_buffer_heads(struct page *page, struct block_device *bdev) { + + struct buffer_head *bh; + struct buffer_head *head; + + if (page_has_buffers(page)) { + //printk(KERN_DEBUG "after if\n"); + + bh = page_buffers(page); + head = bh; + do { + //printk(KERN_DEBUG "bstate: %lu\n", bh->b_state); + bh->b_bdev = bdev; + bh = bh->b_this_page; + } while (bh != head); + } + + return 0; +} +/* +static struct block_device* fat_get_bdev_of_page(struct page *page) +{ + struct buffer_head *bh; + + if (page_has_buffers(page)) { + bh = page_buffers(page); + return bh->b_bdev; + } else { + return NULL; + } +} + + +static int fat_dont_writepage(struct page *page, struct writeback_control *wbc) { + // see mm/page-writeback.c line 1387 + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return 0; +} +*/ + +/* +static int fat_writepage_to_device(struct page *page, struct block_device *bdev){ + struct block_device *orig_bdev; + struct buffer_head *bh; + struct buffer_head *head; + + orig_bdev = fat_get_bdev_of_page(page); + + fat_set_device_in_buffer_heads(page, bdev); + + if (page_has_buffers(page)) { + bh = page_buffers(page); + head = bh; + do { + lock_buffer(bh); + //end_buffer_async_write + mark_buffer_async_write(bh); + set_buffer_mapped(bh); + submit_bh(WRITE, bh); + bh = bh->b_this_page; + } while (bh != head); + } + + fat_set_device_in_buffer_heads(page, orig_bdev); + + return 0; // :-D +} +*/ + +static void fat_set_buffer_head_state(struct buffer_head *head, unsigned long* state) { + struct buffer_head *bh; + unsigned int i = 0; + + bh = head; + + do { + bh->b_state = state[i]; + bh = bh->b_this_page; + i++; + //test the correct behaviour of the ssd in case of out of bounds writing + //bh->b_blocknr += 62533296; + } while (bh != head); +} + +static void fat_preserve_buffers(struct page *page) { + struct buffer_head *bh; + struct buffer_head *head; + + get_page(page); + + if (page_has_buffers(page)) { + bh = page_buffers(page); + head = bh; + do { + get_bh(bh); + bh = bh->b_this_page; + } while (bh != head); + } +} + +static void fat_release_buffers(struct page *page) { + struct buffer_head *bh; + struct buffer_head *head; + + put_page(page); + + if (page_has_buffers(page)) { + bh = page_buffers(page); + head = bh; + do { + put_bh(bh); + bh = bh->b_this_page; + } while (bh != head); + } +} + +static int fat_writepage_to_device(struct page *page, struct writeback_control *wbc, struct block_device *bdev, unsigned long* state) { + + // get the device + blkdev_get(bdev, FMODE_READ | FMODE_WRITE, 0); + + // lock the page if not already the case + trylock_page(page); + + + fat_set_buffer_head_state(page_buffers(page), state); + + fat_set_device_in_buffer_heads(page, bdev); + return block_write_full_page(page, fat_get_block, wbc); +} + +static int fat_writepage_to_cache(struct page *page, struct writeback_control *wbc, unsigned long* state) { + struct block_device *bdev; + bdev = lookup_bdev("/dev/sdb1"); + + return fat_writepage_to_device(page, wbc, bdev, state); +} + +static int fat_writepage_to_mass(struct page *page, struct writeback_control *wbc, unsigned long* state) { + struct block_device *bdev; + bdev = lookup_bdev("/dev/sda1"); + + return fat_writepage_to_device(page, wbc, bdev, state); +} + +static int fat_is_cache_out_of_bounds(struct page *page) { + struct buffer_head *bh; + struct buffer_head *head; + + sector_t max_block_end = 0; + sector_t cur_block_end = 0; + + if (page_has_buffers(page)) { + bh = page_buffers(page); + head = bh; + do { + cur_block_end = bh->b_blocknr + bh->b_size / bh->b_bdev->bd_block_size + 1; + if (cur_block_end > max_block_end) + max_block_end = cur_block_end; + bh = bh->b_this_page; + } while (bh != head); + } + + return max_block_end > 62533296; +} + +static void fat_preserve_state_of_buffers(struct buffer_head* head, unsigned long* state) { + + struct buffer_head* bh; + unsigned int i = 0; + bh = head; + + do { + state[i] = bh->b_state; + i++; + bh = bh->b_this_page; + } while (bh != head); +} + +static int fat_writepage(struct page *page, struct writeback_control *wbc) +{ + /* + struct buffer_head *bh; + struct buffer_head *head; + + if (page_has_buffers(page)) { + bh = page_buffers(page); + head = bh; + do { + bh->b_blocknr += 62533296; + bh = bh->b_this_page; + } while (bh != head); + } + */ + + //printk(KERN_DEBUG "writepage()\n"); + + // mark buffers and page as used + //printk(KERN_DEBUG "preserve buffers\n"); + unsigned long state[8]; + + fat_preserve_buffers(page); + + fat_preserve_state_of_buffers(page_buffers(page), state); + + fat_writepage_to_mass(page, wbc, state); + + if (!fat_is_cache_out_of_bounds(page)) { + //printk(KERN_ERR "on cache\n"); + wait_on_page_writeback(page); + + fat_writepage_to_cache(page, wbc, state); + } + + fat_release_buffers(page); + + return 0; } +/* removed static int fat_writepages(struct address_space *mapping, - struct writeback_control *wbc) + struct writeback_control *wbc) { return mpage_writepages(mapping, wbc, fat_get_block); } +*/ static int fat_readpage(struct file *file, struct page *page) { - return mpage_readpage(page, fat_get_block); + //printk(KERN_ERR "readpage()\n"); + //BUG_ON(page_has_buffers(page)); // Fails! + + return mpage_readpage(page, fat_get_block_read); } +/* removed static int fat_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { return mpage_readpages(mapping, pages, nr_pages, fat_get_block); } +*/ + + +// CODE of assignment ends here static void fat_write_failed(struct address_space *mapping, loff_t to) { @@ -158,6 +456,7 @@ int err; *pagep = NULL; + //printk(KERN_ERR "damn\n"); err = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, fat_get_block, &MSDOS_I(mapping->host)->mmu_private); @@ -233,9 +532,9 @@ static const struct address_space_operations fat_aops = { .readpage = fat_readpage, - .readpages = fat_readpages, + //.readpages = fat_readpages, .writepage = fat_writepage, - .writepages = fat_writepages, + //.writepages = fat_writepages, .write_begin = fat_write_begin, .write_end = fat_write_end, .direct_IO = fat_direct_IO, diff -Nur sauberer_kernel/linux-3.1.4/fs/fat/namei_vfat.c unser_kernel/linux-3.1.4/fs/fat/namei_vfat.c --- sauberer_kernel/linux-3.1.4/fs/fat/namei_vfat.c 2011-11-28 23:48:14.000000000 +0100 +++ unser_kernel/linux-3.1.4/fs/fat/namei_vfat.c 2012-02-05 14:36:47.966831541 +0100 @@ -21,8 +21,32 @@ #include #include #include +#include +#include #include "fat.h" +//struct super_block *sb_hdd; + +// Copy paste! +static int set_bdev_super(struct super_block *s, void *data) +{ + s->s_bdev = data; + s->s_dev = s->s_bdev->bd_dev; + + /* + * We set the bdi here to the queue backing, file systems can + * overwrite this in ->fill_super() + */ + s->s_bdi = &bdev_get_queue(s->s_bdev)->backing_dev_info; + return 0; +} + +static int test_bdev_super(struct super_block *s, void *data) +{ + return (void *)s->s_bdev == data; +} +// Copy paste end! + /* * If new entry was created in the parent, it could create the 8.3 * alias (the shortname of logname). So, the parent may have the @@ -1079,6 +1103,14 @@ int flags, const char *dev_name, void *data) { + //fmode_t mode = FMODE_READ | FMODE_EXCL | FMODE_WRITE; + //fmode_t mode = FMODE_READ | FMODE_WRITE; + //struct block_device *bdev = blkdev_get_by_path("/dev/sda1", mode, fs_type); + //cache_bdev = lookup_bdev("/dev/sdb1"); + //blkdev_get(cache_bdev, FMODE_READ | FMODE_WRITE, 0); + //sb_hdd = sget(fs_type, test_bdev_super, set_bdev_super, bdev); + printk(KERN_ERR "mounting\n"); + return mount_bdev(fs_type, flags, dev_name, data, vfat_fill_super); } diff -Nur sauberer_kernel/linux-3.1.4/include/scsi/scsi_device.h unser_kernel/linux-3.1.4/include/scsi/scsi_device.h --- sauberer_kernel/linux-3.1.4/include/scsi/scsi_device.h 2011-11-28 23:48:14.000000000 +0100 +++ unser_kernel/linux-3.1.4/include/scsi/scsi_device.h 2012-02-05 14:38:35.875386855 +0100 @@ -174,6 +174,15 @@ struct scsi_dh_data *scsi_dh_data; enum scsi_device_state sdev_state; unsigned long sdev_data[0]; + + /* new power management stuff */ + unsigned long break_even_period; + unsigned long last_access; + int power_mode; + int new_power_mode; + /* DDT/ES */ + unsigned long busy_period; + unsigned long first_access; } __attribute__((aligned(sizeof(unsigned long)))); struct scsi_dh_devlist { diff -Nur sauberer_kernel/linux-3.1.4/scsi_power/Makefile unser_kernel/linux-3.1.4/scsi_power/Makefile --- sauberer_kernel/linux-3.1.4/scsi_power/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ unser_kernel/linux-3.1.4/scsi_power/Makefile 2012-02-05 14:36:35.532781463 +0100 @@ -0,0 +1,8 @@ + +obj-m += scsi_power.o + +all: + make -C /home/power/pm2/linux-3.1.4 SUBDIRS=$(PWD) modules + +clean: + make -C /home/power/pm2/linux-3.1.4 SUBDIRS=$(PWD) clean diff -Nur sauberer_kernel/linux-3.1.4/scsi_power/scsi_power.c unser_kernel/linux-3.1.4/scsi_power/scsi_power.c --- sauberer_kernel/linux-3.1.4/scsi_power/scsi_power.c 1970-01-01 01:00:00.000000000 +0100 +++ unser_kernel/linux-3.1.4/scsi_power/scsi_power.c 2012-02-05 14:36:35.532781463 +0100 @@ -0,0 +1,263 @@ +#include /* Needed by all modules */ +#include /* Needed by all modules */ +#include /* Needed for KERN_INFO */ +#include /* Kernel threads */ +#include /* Thread sleep */ +#include +#include +#include +#include +#include +#include +#include /* HDIO_DRIVE_CMD */ +#include +#include +#include + +enum { + ATA_OP_STANDBYNOW1 = 0xe0, + ATA_OP_STANDBYNOW2 = 0x94, + ATA_OP_CHECKPOWERMODE2 = 0x98, + ATA_OP_CHECKPOWERMODE1 = 0xe5, +}; + +enum { + SCSI_STANDBY = 0, + SCSI_RUNNING = 1, + SCSI_CHANGED = 2, + SCSI_NOCHANGE = 3 +}; + +enum { + T_1 = 2 * HZ, + T_2 = 1 * HZ +}; + +static struct task_struct *ts; +static struct block_device *bdev; +static struct block_device *cache_bdev; +//static struct block_device *cache1_bdev; +static struct gendisk *disk; +static struct gendisk *cache_disk; +static struct timer_list timer; +//static struct ide_drive_t *ide_drive; // We're not using IDE... +static struct scsi_device *scsi_device; +static make_request_fn* oldMRfn; + +//static unsigned long break_even_period; +//static unsigned long last_access; +//static unsigned int power_mode; /* 0=standby, 1=running */ +//static unsigned int new_power_mode; /* 0=standby, 1=running, 2=changed, 3=no_change */ + +DEFINE_MUTEX(power_mutex); + +void scsi_call_standby(void) +{ + printk(KERN_INFO "scsi_call_standby()\n"); + + scsi_device->new_power_mode = SCSI_STANDBY; + mutex_unlock(&power_mutex); +} + +void scsi_check_for_standby(unsigned long duration) +{ + int is_ddt_time = time_after(jiffies, scsi_device->last_access + scsi_device->break_even_period * HZ); + int is_es_time = time_after(jiffies, scsi_device->first_access + scsi_device->busy_period) + && time_after(scsi_device->first_access + scsi_device->busy_period + T_1, jiffies) + && time_after(jiffies, scsi_device->last_access + T_2); + + printk(KERN_INFO "jif: %lu, la: %lu, be: %lu, pm: %d, npm: %d, bp: %lu, fa: %lu", jiffies, scsi_device->last_access, scsi_device->break_even_period, scsi_device->power_mode, scsi_device->new_power_mode, scsi_device->busy_period, scsi_device->first_access); + + //if (scsi_device->power_mode != SCSI_STANDBY && is_ddt_time) { // regular DDT + if (scsi_device->power_mode != SCSI_STANDBY && (is_ddt_time || is_es_time)) { // DDT/ES + // wait. + //mod_timer(&timer, jiffies + 5 * HZ); + scsi_call_standby(); + } else { + mod_timer(&timer, jiffies + duration); + } +} + +void scsi_check_state(void) +{ + __u8 args[4] = {ATA_OP_CHECKPOWERMODE1,0,0,0}; + const char *state = "unknown"; + + //printk(KERN_INFO "bdev: %li\n", IS_ERR(bdev)); + //printk(KERN_INFO "disk: %p\n", disk); + //printk(KERN_INFO "fops: %p\n", disk->fops); + //printk(KERN_INFO "ioctl: %p\n", disk->fops->ioctl); + + if (disk->fops->ioctl) { + disk->fops->ioctl(bdev, 0, HDIO_DRIVE_CMD, (unsigned long) args); + args[0] = ATA_OP_CHECKPOWERMODE2; + disk->fops->ioctl(bdev, 0, HDIO_DRIVE_CMD, (unsigned long) args); + } + + switch (args[2]) { + case 0x00: + case 0x40: + case 0x41: + state = "standby"; + scsi_device->power_mode = SCSI_STANDBY; + break; + case 0x80: + case 0xff: + state = "active/idle"; + scsi_device->power_mode = SCSI_RUNNING; + break; + } + + scsi_device->new_power_mode = SCSI_NOCHANGE; + + printk(KERN_INFO " drive state is: %s\n", state); + printk(KERN_INFO " drive state is: %d\n", args[2]); +} + +void scsi_standby(void) +{ + struct backing_dev_info *bdi; + struct backing_dev_info *cache_bdi; + __u8 args[4] = {ATA_OP_STANDBYNOW1,0,0,0}; + + printk(KERN_INFO "scsi_standby()\n"); + + // Write back all dirty blocks first + //sync_filesystem(cache1_bdev->bd_super); + if (laptop_mode) { + printk(KERN_INFO "writing back dirty blocks\n"); + //bdi = &disk->queue->backing_dev_info; + //mod_timer(&bdi->laptop_mode_wb_timer, jiffies/* + laptop_mode*/); // immediately wake up + cache_bdi = &cache_disk->queue->backing_dev_info; + mod_timer(&cache_bdi->laptop_mode_wb_timer, jiffies/* + laptop_mode*/); // immediately wake up + + set_current_state(TASK_INTERRUPTIBLE); + do { + msleep(100); + } while(test_bit(BDI_writeback_running, &cache_bdi->state)); + //while (time_before(jiffies, scsi_device->last_access + 5 * HZ)) { + // msleep(1000); /* sleep for a second */ + //} + //cache_bdi->bdi_lock + set_current_state(TASK_RUNNING); + } + + //set_current_state(TASK_RUNNING); + if (disk->fops->ioctl) { + disk->fops->ioctl(bdev, 0, HDIO_DRIVE_CMD, (unsigned long) args); + args[0] = ATA_OP_STANDBYNOW2; + disk->fops->ioctl(bdev, 0, HDIO_DRIVE_CMD, (unsigned long) args); + } + + scsi_device->power_mode = SCSI_STANDBY; + scsi_device->new_power_mode = SCSI_NOCHANGE; + + mod_timer(&timer, jiffies + 1 * HZ); +} + +int scsi_power_task(void *data) +{ + while(1) { + set_current_state(TASK_INTERRUPTIBLE); + mutex_lock(&power_mutex); + // msleep(1000); /* sleep for a second */ + // ssleep(1); /* does the same */ + if (kthread_should_stop()) { + break; + } + + set_current_state(TASK_RUNNING); + + printk(KERN_INFO "Thread running!\n"); + + if (scsi_device->new_power_mode == SCSI_CHANGED) { + scsi_check_state(); + } else if (scsi_device->new_power_mode == SCSI_STANDBY) { + //set_current_state(TASK_INTERRUPTIBLE); + scsi_standby(); + } + } + + return 0; +} + +int scsi_device_request_fn(struct request_queue *q, struct bio *bio) +{ + //printk(KERN_INFO "request\n"); + + if (time_after(jiffies, scsi_device->last_access + scsi_device->break_even_period * HZ)) { + scsi_device->busy_period = scsi_device->last_access - scsi_device->first_access; + scsi_device->first_access = jiffies; + } + + scsi_device->last_access = jiffies; + scsi_device->power_mode = SCSI_RUNNING; + scsi_device->new_power_mode = SCSI_NOCHANGE; + + return oldMRfn(q, bio); +} + +static int scsi_power_init(void) +{ + printk(KERN_ALERT "Initializint scsi_power v1.1 \n"); + + bdev = lookup_bdev("/dev/sda"); + cache_bdev = lookup_bdev("/dev/sdb"); + //cache1_bdev = lookup_bdev("/dev/sdb1"); + blkdev_get(bdev, FMODE_READ, 0); + disk = bdev->bd_disk; + cache_disk = cache_bdev->bd_disk; + scsi_device = disk->queue->queuedata; /* we must be sure it's actually an scsi device */ + + scsi_device->break_even_period = 20; + scsi_device->power_mode = SCSI_RUNNING; + scsi_device->new_power_mode = SCSI_CHANGED; // Check device state at start + + printk(KERN_ALERT "vendor: %s\n", scsi_device->vendor); + + scsi_device->last_access = jiffies; + scsi_device->first_access = jiffies; + scsi_device->busy_period = 0; + oldMRfn = scsi_device->request_queue->make_request_fn; + scsi_device->request_queue->make_request_fn = scsi_device_request_fn; + + mutex_init(&power_mutex); + //mutex_lock(&power_mutex); /* necessary so thread doesn't run at start */ + + /* + * Create a thread and run it. + */ + + ts = kthread_run(scsi_power_task, NULL, "scsi_power_task"); + + init_timer(&timer); + timer.function = scsi_check_for_standby; + timer.data = 1*HZ; + timer.expires = jiffies + 1*HZ; + add_timer(&timer); + + /* + * A non 0 return means init_module failed; module can't be loaded. + */ + return 0; +} + +static void scsi_power_exit(void) +{ + printk(KERN_INFO "Exitting scsi_power v1.1\n"); + + scsi_device->request_queue->make_request_fn = oldMRfn; + + mutex_unlock(&power_mutex); + + del_timer(&timer); + + /* + * Terminate the thread. + */ + kthread_stop(ts); +} + +MODULE_LICENSE ("Dual BSD/GPL"); +module_init(scsi_power_init); +module_exit(scsi_power_exit);