--- zzzz-none-000/linux-2.6.13.1/mm/filemap.c 2005-09-10 02:42:58.000000000 +0000 +++ ohio-7170-487/linux-2.6.13.1/mm/filemap.c 2006-06-16 12:40:33.000000000 +0000 @@ -37,6 +37,176 @@ #include #include +/****************************************************************************** + * <<<< 20060608 =OS= -- sync-on-demand + ******************************************************************************/ + +#include + +/* + * __atomic_xchg - read and set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * (as introduced in 2.6.16, 20060608 =OS=) + */ +#define __atomic_xchg(v, i) (xchg(&((v)->counter), i)) + +#define CONFIG_MAX_DIRTY_FILE_PAGES 40 /* <<<< see generic_file_aio_write() */ +#define CONFIG_MAX_DIRTY_FILE_PAGES_2 10 /* <<<< see generic_file_aio_write() */ +#define CONFIG_MAX_DIRTY_REST_PAGES 20 /* <<<< see fs/open.c, filp_close() */ +#define CONFIG_FILE_SYNC_THRESHOLD 2048 /* kBytes (obsolete) */ +#define CONFIG_FILE_SYNC_MULTIPLIER 3 /* File sync threshold (sync <-> async): */ +#define CONFIG_FILE_SYNC_DIVISOR 2 /* (pages_min*multiplier)/divisor */ + +#define K(x) ((x) << (PAGE_SHIFT - 10)) + +typedef struct { + + const char * name; /* Name of /proc writable parameter */ + unsigned * var; /* Pointer to its unsigned variable */ + atomic_t * dirty; /* Pointer to its atomic update flag */ +} entry_t, * entry_p; + +atomic_t flag_file_sync = ATOMIC_INIT(1); /* Safety first: Let's start in sync mode */ +atomic_t f_global_dirt = ATOMIC_INIT(0); +unsigned max_rest_dirty_pages = CONFIG_MAX_DIRTY_REST_PAGES; +unsigned max_file_dirty_pages = CONFIG_MAX_DIRTY_FILE_PAGES; +unsigned max_file_dirty_pages2 = CONFIG_MAX_DIRTY_FILE_PAGES_2; +unsigned file_sync_page_mul = CONFIG_FILE_SYNC_MULTIPLIER; +unsigned file_sync_page_div = CONFIG_FILE_SYNC_DIVISOR; +atomic_t file_sync_dirty = ATOMIC_INIT(1); + +static entry_t varray[] = { +#define ENTRY(x,f) {#x,&x,f} + + ENTRY(max_rest_dirty_pages, NULL), + ENTRY(max_file_dirty_pages, NULL), + ENTRY(max_file_dirty_pages2, NULL), + ENTRY(file_sync_page_mul, &file_sync_dirty), + ENTRY(file_sync_page_div, &file_sync_dirty) + +#undef ENTRY +} ; +#define NUM_ENTRIES (sizeof(varray)/sizeof(varray[0])) + +static inline unsigned min_threshold(void) { + unsigned nmp = K(nr_min_pages()); + + return (nmp * file_sync_page_mul) / file_sync_page_div; +} /* min_threshold */ + +int file_io_controller (void) { + static unsigned file_sync_threshold; + int flag; + + if (__atomic_xchg (&file_sync_dirty, 0)) + file_sync_threshold = min_threshold (); + flag = K(nr_free_pages ()) < file_sync_threshold; + atomic_set (&flag_file_sync, flag); +/* + * if (flag != __atomic_xchg (&flag_file_sync, flag)) { + * printk (KERN_INFO "<<<< Now %s mode >>>>\n", flag ? "sync" : "async"); + * } + */ + return flag; +} /* file_io_controller */ + +/*----------------------------------------------------------------------------*/ +#if defined (CONFIG_PROC_FS) +#include + +static struct proc_dir_entry * proc_file = NULL; + +static void sod_var_assign (entry_p ep, char * valp) { + unsigned val = 0; + char * vp = valp; + char * cp; + + while ((*vp == ' ') || (*vp == '\t')) + ++vp; + cp = vp; + do { + if ((*vp < '0') || ('9' < *vp)) break; + val = val * 10 + (*vp - '0'); + } while (*++vp != '\0'); + if ((*vp != ' ') && (*vp != '\t') && (*vp != '\n') && (*vp != '\0')) { + printk (KERN_INFO "Invalid number format (%s)!\n", ep->name); + return; + } + /* printk (KERN_INFO "/proc/sync-on-demand.%s: %u --> %u\n", ep->name, *(ep->var), val); */ + *(ep->var) = val; + if (ep->dirty != NULL) + atomic_set (ep->dirty, 1); +} /* sod_var_assign */ + +static int sod_read (char * buf, char ** start, off_t offset, int size, int * eof, void * data) { + int bytes_written = 0; + int ret, i; + + /* read/write variables/values: */ + for (i = 0; i < NUM_ENTRIES; i++) { + ret = snprintf ( + buf + bytes_written, (size - bytes_written), + "%-30s%u\n", + varray[i].name, *(varray[i].var) + ); + bytes_written += (ret > (size - bytes_written)) ? (size - bytes_written) : ret; + } + + /* read-only variables/values: */ + ret = snprintf ( + buf + bytes_written, (size - bytes_written), + "%-30s%u kB\n", + "(file_sync_threshold)", min_threshold () + ); + bytes_written += (ret > (size - bytes_written)) ? (size - bytes_written) : ret; + + *eof = 1; + return bytes_written; +} /* sod_read */ + +static ssize_t sod_write (struct file * filp, const char __user * buf, unsigned long count, void * data) { + char * kbuf; + unsigned long not_copied; + int i, l; + + if (NULL == (kbuf = kmalloc (count + 1, GFP_KERNEL))) + return -ENOMEM; + kbuf[count] = (char) 0; + not_copied = copy_from_user (kbuf, buf, count); + for (i = 0; i < NUM_ENTRIES; i++) { + l = strlen (varray[i].name); + if (l >= count) continue; + if (0 == strncmp (kbuf, varray[i].name, l)) { + if (kbuf[l] != ' ') continue; + sod_var_assign (&varray[i], &kbuf[l]); + break; + } + } + kfree (kbuf); + return (count - not_copied); +} /* sod_write */ + +void setup_sod_procfs (void) { + + proc_file = create_proc_entry ("sync-on-demand", S_IRUSR|S_IWUSR, NULL); + if (proc_file == NULL) return; + proc_file->read_proc = sod_read; + proc_file->write_proc = sod_write; + proc_file->data = NULL; +} /* setup_procfs */ + +void shutdown_sod_procfs (void) { + + if (proc_file != NULL) + remove_proc_entry ("sync-on-demand", NULL); +} /* shutdown_procfs */ +#endif + +/******************************************************************************/ + + /* * Shared mappings implemented 30.11.1994. It's not fully working yet, * though. @@ -1880,6 +2050,9 @@ size_t iov_base = 0; /* offset in the current iovec */ char __user *buf; + unsigned long pnd; /* <<<< 20060608 =OS= */ + struct page_state ps; + pagevec_init(&lru_pvec, 0); /* @@ -1892,6 +2065,10 @@ buf = cur_iov->iov_base + iov_base; } + /* <<<< 20060608 =OS= */ + get_page_state (&ps); + pnd = ps.nr_dirty; + do { unsigned long index; unsigned long offset; @@ -1965,6 +2142,7 @@ if (unlikely(copied != bytes)) if (status >= 0) status = -EFAULT; + unlock_page(page); mark_page_accessed(page); page_cache_release(page); @@ -1998,6 +2176,13 @@ status = filemap_write_and_wait(mapping); pagevec_lru_add(&lru_pvec); + + get_page_state (&ps); /* <<<< 20060608 =OS= */ + if (ps.nr_dirty > pnd) { + file->f_dirt += ps.nr_dirty - pnd; + } + file_io_controller(); + return written ? written : status; } EXPORT_SYMBOL(generic_file_buffered_write); @@ -2138,11 +2323,26 @@ struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; ssize_t ret; + int err, flag, limit; struct iovec local_iov = { .iov_base = (void __user *)buf, .iov_len = count }; BUG_ON(iocb->ki_pos != pos); + /* <<<< 20060602 =OS= */ + flag = atomic_read(&flag_file_sync); + limit = flag ? max_file_dirty_pages2 : max_file_dirty_pages; + if (file->f_dirt > limit) { + if (flag) { + /* Synchronously writeback this file... */ + err = file_fsync (file, file->f_dentry, 1); + } + wakeup_pdflush (0); + atomic_set (&f_global_dirt, 0); /* Nothing but an asumption! :-) */ + file->f_dirt = err = 0; /* Ditto */ + } + file_io_controller(); + down(&inode->i_sem); ret = __generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);