diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
commit | 51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch) | |
tree | 5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /fs | |
parent | 52273a23c9a84336b93a35e4847fc88fac7eb0e4 (diff) |
Merge with Linux 2.3.7.
WARNING: 2.3.7 is known to eat filesystems for breakfast and little
children for lunch, so if you try this on your machine make backups
first ...
Diffstat (limited to 'fs')
71 files changed, 2283 insertions, 1814 deletions
diff --git a/fs/Config.in b/fs/Config.in index 830e18ffe..a2ddc3916 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -90,9 +90,6 @@ if [ "$CONFIG_INET" = "y" ]; then fi fi tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS - if [ "$CONFIG_SMB_FS" != "n" ]; then - bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95 - fi fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 3a1c78ef0..ee08ff451 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -63,7 +63,6 @@ struct inode_operations affs_dir_inode_operations = { NULL, /* truncate */ NULL, /* permissions */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/affs/file.c b/fs/affs/file.c index 1961b4ec3..bb1ce69c8 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -80,7 +80,6 @@ struct inode_operations affs_file_inode_operations = { affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; @@ -121,7 +120,6 @@ struct inode_operations affs_file_inode_operations_ofs = { affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 425df6577..f6ccf8419 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -79,7 +79,6 @@ struct inode_operations autofs_dir_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/autofs/root.c b/fs/autofs/root.c index c1b57ec6e..011e3286f 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -60,7 +60,6 @@ struct inode_operations autofs_root_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index a4cb5154f..0e46db365 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -55,6 +55,5 @@ struct inode_operations autofs_symlink_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 33560caa4..89711607b 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -60,13 +60,13 @@ struct inode_operations bad_inode_ops = EIO_ERROR, /* rename */ EIO_ERROR, /* readlink */ bad_follow_link, /* follow_link */ + EIO_ERROR, /* bmap */ EIO_ERROR, /* readpage */ EIO_ERROR, /* writepage */ - EIO_ERROR, /* bmap */ + EIO_ERROR, /* flushpage */ EIO_ERROR, /* truncate */ EIO_ERROR, /* permission */ EIO_ERROR, /* smap */ - EIO_ERROR, /* update_page */ EIO_ERROR /* revalidate */ }; diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index ae22c4900..2b886a147 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -59,11 +59,7 @@ static void set_brk(unsigned long start, unsigned long end) static int dump_write(struct file *file, const void *addr, int nr) { - int r; - down(&file->f_dentry->d_inode->i_sem); - r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; - up(&file->f_dentry->d_inode->i_sem); - return r; + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; } #define DUMP_WRITE(addr, nr) \ diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ffc8c957d..bd3c8f490 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -918,11 +918,7 @@ static int load_elf_library(int fd) */ static int dump_write(struct file *file, const void *addr, int nr) { - int r; - down(&file->f_dentry->d_inode->i_sem); - r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; - up(&file->f_dentry->d_inode->i_sem); - return r; + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; } static int dump_seek(struct file *file, off_t off) diff --git a/fs/block_dev.c b/fs/block_dev.c index 13b3f534d..664522ab8 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -124,6 +124,7 @@ ssize_t block_write(struct file * filp, const char * buf, } buffercount=0; } + balance_dirty(dev); if(write_error) break; } diff --git a/fs/buffer.c b/fs/buffer.c index 0c0d8d87e..75f6486a0 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -24,6 +24,8 @@ * - RMK */ +#include <linux/sched.h> +#include <linux/fs.h> #include <linux/malloc.h> #include <linux/locks.h> #include <linux/errno.h> @@ -113,7 +115,7 @@ union bdflush_param { /* These are the min and max parameter values that we will allow to be assigned */ int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; -int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5}; +int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,1000, 6000*HZ, 6000*HZ, 2047, 5}; void wakeup_bdflush(int); @@ -422,7 +424,25 @@ void invalidate_buffers(kdev_t dev) #define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) #define hash(dev,block) hash_table[_hashfn(dev,block)] -static inline void remove_from_hash_queue(struct buffer_head * bh) +static void insert_into_hash_list(struct buffer_head * bh) +{ + bh->b_next = NULL; + bh->b_pprev = NULL; + if (bh->b_dev) { + struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr); + struct buffer_head *next = *bhp; + + if (next) { + bh->b_next = next; + next->b_pprev = &bh->b_next; + } + *bhp = bh; + bh->b_pprev = bhp; + nr_hashed_buffers++; + } +} + +static void remove_from_hash_queue(struct buffer_head * bh) { struct buffer_head **pprev = bh->b_pprev; if (pprev) { @@ -433,16 +453,43 @@ static inline void remove_from_hash_queue(struct buffer_head * bh) } *pprev = next; bh->b_pprev = NULL; + nr_hashed_buffers--; } - nr_hashed_buffers--; } -static inline void remove_from_lru_list(struct buffer_head * bh) +static void insert_into_lru_list(struct buffer_head * bh) { - if (!(bh->b_prev_free) || !(bh->b_next_free)) - panic("VFS: LRU block list corrupted"); + struct buffer_head **bhp = &lru_list[bh->b_list]; + if (bh->b_dev == B_FREE) - panic("LRU list corrupted"); + BUG(); + + if(!*bhp) { + *bhp = bh; + bh->b_prev_free = bh; + } + + if (bh->b_next_free) + panic("VFS: buffer LRU pointers corrupted"); + + bh->b_next_free = *bhp; + bh->b_prev_free = (*bhp)->b_prev_free; + (*bhp)->b_prev_free->b_next_free = bh; + (*bhp)->b_prev_free = bh; + + nr_buffers++; + nr_buffers_type[bh->b_list]++; +} + +static void remove_from_lru_list(struct buffer_head * bh) +{ + if (!(bh->b_prev_free) || !(bh->b_next_free)) + return; + + if (bh->b_dev == B_FREE) { + printk("LRU list corrupted"); + *(int*)0 = 0; + } bh->b_prev_free->b_next_free = bh->b_next_free; bh->b_next_free->b_prev_free = bh->b_prev_free; @@ -451,9 +498,12 @@ static inline void remove_from_lru_list(struct buffer_head * bh) if (lru_list[bh->b_list] == bh) lru_list[bh->b_list] = NULL; bh->b_next_free = bh->b_prev_free = NULL; + + nr_buffers--; + nr_buffers_type[bh->b_list]--; } -static inline void remove_from_free_list(struct buffer_head * bh) +static void remove_from_free_list(struct buffer_head * bh) { int isize = BUFSIZE_INDEX(bh->b_size); if (!(bh->b_prev_free) || !(bh->b_next_free)) @@ -475,21 +525,20 @@ static inline void remove_from_free_list(struct buffer_head * bh) static void remove_from_queues(struct buffer_head * bh) { - if(bh->b_dev == B_FREE) { - remove_from_free_list(bh); /* Free list entries should not be - in the hash queue */ - return; - } - nr_buffers_type[bh->b_list]--; + if (bh->b_dev == B_FREE) + BUG(); remove_from_hash_queue(bh); remove_from_lru_list(bh); } -static inline void put_last_free(struct buffer_head * bh) +static void put_last_free(struct buffer_head * bh) { if (bh) { struct buffer_head **bhp = &free_list[BUFSIZE_INDEX(bh->b_size)]; + if (bh->b_count) + BUG(); + bh->b_dev = B_FREE; /* So it is obvious we are on the free list. */ /* Add to back of free list. */ @@ -505,47 +554,6 @@ static inline void put_last_free(struct buffer_head * bh) } } -static void insert_into_queues(struct buffer_head * bh) -{ - /* put at end of free list */ - if(bh->b_dev == B_FREE) { - put_last_free(bh); - } else { - struct buffer_head **bhp = &lru_list[bh->b_list]; - - if(!*bhp) { - *bhp = bh; - bh->b_prev_free = bh; - } - - if (bh->b_next_free) - panic("VFS: buffer LRU pointers corrupted"); - - bh->b_next_free = *bhp; - bh->b_prev_free = (*bhp)->b_prev_free; - (*bhp)->b_prev_free->b_next_free = bh; - (*bhp)->b_prev_free = bh; - - nr_buffers_type[bh->b_list]++; - - /* Put the buffer in new hash-queue if it has a device. */ - bh->b_next = NULL; - bh->b_pprev = NULL; - if (bh->b_dev) { - struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr); - struct buffer_head *next = *bhp; - - if (next) { - bh->b_next = next; - next->b_pprev = &bh->b_next; - } - *bhp = bh; - bh->b_pprev = bhp; - } - nr_hashed_buffers++; - } -} - struct buffer_head * find_buffer(kdev_t dev, int block, int size) { struct buffer_head * next; @@ -636,6 +644,7 @@ void set_blocksize(kdev_t dev, int size) if (bh->b_size == size) continue; bhnext->b_count++; + bh->b_count++; wait_on_buffer(bh); bhnext->b_count--; if (bh->b_dev == dev && bh->b_size != size) { @@ -644,9 +653,10 @@ void set_blocksize(kdev_t dev, int size) clear_bit(BH_Req, &bh->b_state); bh->b_flushtime = 0; } + if (--bh->b_count) + continue; remove_from_queues(bh); - bh->b_dev=B_FREE; - insert_into_queues(bh); + put_last_free(bh); } } } @@ -666,7 +676,6 @@ static void refill_freelist(int size) void init_buffer(struct buffer_head *bh, kdev_t dev, int block, bh_end_io_t *handler, void *dev_id) { - bh->b_count = 1; bh->b_list = BUF_CLEAN; bh->b_flushtime = 0; bh->b_dev = dev; @@ -702,7 +711,7 @@ repeat: if (!buffer_dirty(bh)) { bh->b_flushtime = 0; } - return bh; + goto out; } isize = BUFSIZE_INDEX(size); @@ -716,9 +725,13 @@ get_free: * and that it's unused (b_count=0), unlocked, and clean. */ init_buffer(bh, dev, block, end_buffer_io_sync, NULL); - bh->b_state=0; - insert_into_queues(bh); - return bh; + bh->b_count = 1; + bh->b_state = 0; + + /* Insert the buffer into the regular lists */ + insert_into_lru_list(bh); + insert_into_hash_list(bh); + goto out; /* * If we block while refilling the free list, somebody may @@ -729,6 +742,8 @@ refill: if (!find_buffer(dev,block,size)) goto get_free; goto repeat; +out: + return bh; } void set_writetime(struct buffer_head * buf, int flag) @@ -746,15 +761,56 @@ void set_writetime(struct buffer_head * buf, int flag) } } - /* * Put a buffer into the appropriate list, without side-effects. */ -static inline void file_buffer(struct buffer_head *bh, int list) +static void file_buffer(struct buffer_head *bh, int list) { - remove_from_queues(bh); + remove_from_lru_list(bh); bh->b_list = list; - insert_into_queues(bh); + insert_into_lru_list(bh); +} + +/* + * if a new dirty buffer is created we need to balance bdflush. + * + * in the future we might want to make bdflush aware of different + * pressures on different devices - thus the (currently unused) + * 'dev' parameter. + */ +void balance_dirty(kdev_t dev) +{ + int dirty = nr_buffers_type[BUF_DIRTY]; + int ndirty = bdf_prm.b_un.ndirty; + + if (dirty > ndirty) { + int wait = 0; + if (dirty > 2*ndirty) + wait = 1; + wakeup_bdflush(wait); + } +} + +atomic_t too_many_dirty_buffers; + +static inline void __mark_dirty(struct buffer_head *bh, int flag) +{ + set_writetime(bh, flag); + refile_buffer(bh); + if (atomic_read(&too_many_dirty_buffers)) + balance_dirty(bh->b_dev); +} + +void __mark_buffer_dirty(struct buffer_head *bh, int flag) +{ + __mark_dirty(bh, flag); +} + +void __atomic_mark_buffer_dirty(struct buffer_head *bh, int flag) +{ + lock_kernel(); + __mark_dirty(bh, flag); + unlock_kernel(); } /* @@ -765,36 +821,19 @@ void refile_buffer(struct buffer_head * buf) { int dispose; - if(buf->b_dev == B_FREE) { + if (buf->b_dev == B_FREE) { printk("Attempt to refile free buffer\n"); return; } + + dispose = BUF_CLEAN; + if (buffer_locked(buf)) + dispose = BUF_LOCKED; if (buffer_dirty(buf)) dispose = BUF_DIRTY; - else if (buffer_locked(buf)) - dispose = BUF_LOCKED; - else - dispose = BUF_CLEAN; - if(dispose != buf->b_list) { - file_buffer(buf, dispose); - if(dispose == BUF_DIRTY) { - int too_many = (nr_buffers * bdf_prm.b_un.nfract/100); - /* This buffer is dirty, maybe we need to start flushing. - * If too high a percentage of the buffers are dirty... - */ - if (nr_buffers_type[BUF_DIRTY] > too_many) - wakeup_bdflush(1); - - /* If this is a loop device, and - * more than half of the buffers are dirty... - * (Prevents no-free-buffers deadlock with loop device.) - */ - if (MAJOR(buf->b_dev) == LOOP_MAJOR && - nr_buffers_type[BUF_DIRTY]*2>nr_buffers) - wakeup_bdflush(1); - } - } + if (dispose != buf->b_list) + file_buffer(buf, dispose); } /* @@ -809,6 +848,7 @@ void __brelse(struct buffer_head * buf) if (buf->b_count) { buf->b_count--; + wake_up(&buffer_wait); return; } printk("VFS: brelse: Trying to free free buffer\n"); @@ -890,7 +930,6 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, /* if (blocks) printk("breada (new) %d blocks\n",blocks); */ - bhlist[0] = bh; j = 1; for(i=1; i<blocks; i++) { @@ -928,7 +967,8 @@ static void put_unused_buffer_head(struct buffer_head * bh) return; } - memset(bh,0,sizeof(*bh)); +// memset(bh, 0, sizeof(*bh)); + bh->b_blocknr = -1; init_waitqueue_head(&bh->b_wait); nr_unused_buffer_heads++; bh->b_next_free = unused_list; @@ -1153,17 +1193,12 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) struct page *page; mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); /* This is a temporary buffer used for page I/O. */ page = mem_map + MAP_NR(bh->b_data); - if (!PageLocked(page)) - goto not_locked; - if (bh->b_count != 1) - goto bad_count; - if (!test_bit(BH_Uptodate, &bh->b_state)) - set_bit(PG_error, &page->flags); + if (!uptodate) + SetPageError(page); /* * Be _very_ careful from here on. Bad things can happen if @@ -1179,69 +1214,63 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) */ save_flags(flags); cli(); - bh->b_count--; - tmp = bh; - do { - if (tmp->b_count) + unlock_buffer(bh); + tmp = bh->b_this_page; + while (tmp != bh) { + if (buffer_locked(tmp)) goto still_busy; tmp = tmp->b_this_page; - } while (tmp != bh); + } /* OK, the async IO on this page is complete. */ - free_async_buffers(bh); restore_flags(flags); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + after_unlock_page(page); + /* + * if none of the buffers had errors then we can set the + * page uptodate: + */ + if (!PageError(page)) + SetPageUptodate(page); + if (page->owner != -1) + PAGE_BUG(page); + page->owner = (int)current; + UnlockPage(page); + return; still_busy: restore_flags(flags); return; - -not_locked: - printk ("Whoops: end_buffer_io_async: async io complete on unlocked page\n"); - return; - -bad_count: - printk ("Whoops: end_buffer_io_async: b_count != 1 on async io.\n"); - return; } -/* - * Start I/O on a page. - * This function expects the page to be locked and may return before I/O is complete. - * You then have to check page->locked, page->uptodate, and maybe wait on page->wait. - */ -int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) +static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) { - struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE]; - int block, nr; + struct buffer_head *head, *bh, *tail; + int block; if (!PageLocked(page)) - panic("brw_page: page not locked for I/O"); - clear_bit(PG_uptodate, &page->flags); - clear_bit(PG_error, &page->flags); + BUG(); + if (page->owner != (int)current) + PAGE_BUG(page); /* * Allocate async buffer heads pointing to this page, just for I/O. - * They do _not_ show up in the buffer hash table! - * They are _not_ registered in page->buffers either! + * They show up in the buffer hash table and are registered in + * page->buffers. */ - bh = create_buffers(page_address(page), size, 1); - if (!bh) { - /* WSH: exit here leaves page->count incremented */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - return -ENOMEM; - } - nr = 0; - next = bh; - do { - struct buffer_head * tmp; + lock_kernel(); + head = create_buffers(page_address(page), size, 1); + unlock_kernel(); + if (page->buffers) + BUG(); + if (!head) + BUG(); + tail = head; + for (bh = head; bh; bh = bh->b_this_page) { block = *(b++); - init_buffer(next, dev, block, end_buffer_io_async, NULL); - set_bit(BH_Uptodate, &next->b_state); + tail = bh; + init_buffer(bh, dev, block, end_buffer_io_async, NULL); /* * When we use bmap, we define block zero to represent @@ -1250,51 +1279,379 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) * two cases. */ if (bmap && !block) { - memset(next->b_data, 0, size); - next->b_count--; - continue; + set_bit(BH_Uptodate, &bh->b_state); + memset(bh->b_data, 0, size); + } + } + tail->b_this_page = head; + get_page(page); + page->buffers = head; + return 0; +} + +/* + * We don't have to release all buffers here, but + * we have to be sure that no dirty buffer is left + * and no IO is going on (no buffer is locked), because + * we have truncated the file and are going to free the + * blocks on-disk.. + */ +int block_flushpage(struct inode *inode, struct page *page, unsigned long offset) +{ + struct buffer_head *head, *bh, *next; + unsigned int curr_off = 0; + + if (!PageLocked(page)) + BUG(); + if (!page->buffers) + return 0; + lock_kernel(); + + head = page->buffers; + bh = head; + do { + unsigned int next_off = curr_off + bh->b_size; + next = bh->b_this_page; + + /* + * is this block fully flushed? + */ + if (offset <= curr_off) { + if (bh->b_blocknr) { + bh->b_count++; + wait_on_buffer(bh); + if (bh->b_dev == B_FREE) + BUG(); + mark_buffer_clean(bh); + bh->b_blocknr = 0; + bh->b_count--; + } + } + curr_off = next_off; + bh = next; + } while (bh != head); + + /* + * subtle. We release buffer-heads only if this is + * the 'final' flushpage. We have invalidated the bmap + * cached value unconditionally, so real IO is not + * possible anymore. + */ + if (!offset) + try_to_free_buffers(page); + + unlock_kernel(); + return 0; +} + +static void create_empty_buffers (struct page *page, + struct inode *inode, unsigned long blocksize) +{ + struct buffer_head *bh, *head, *tail; + + lock_kernel(); + head = create_buffers(page_address(page), blocksize, 1); + unlock_kernel(); + if (page->buffers) + BUG(); + + bh = head; + do { + bh->b_dev = inode->i_dev; + bh->b_blocknr = 0; + tail = bh; + bh = bh->b_this_page; + } while (bh); + tail->b_this_page = head; + page->buffers = head; + get_page(page); +} + +/* + * block_write_full_page() is SMP-safe - currently it's still + * being called with the kernel lock held, but the code is ready. + */ +int block_write_full_page (struct file *file, struct page *page, fs_getblock_t fs_get_block) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + int err, created, i; + unsigned long block, phys, offset; + struct buffer_head *bh, *head; + + if (!PageLocked(page)) + BUG(); + + if (!page->buffers) + create_empty_buffers(page, inode, inode->i_sb->s_blocksize); + head = page->buffers; + + offset = page->offset; + block = offset >> inode->i_sb->s_blocksize_bits; + + // FIXME: currently we assume page alignment. + if (offset & (PAGE_SIZE-1)) + BUG(); + + bh = head; + i = 0; + do { + if (!bh) + BUG(); + + if (!bh->b_blocknr) { + err = -EIO; + down(&inode->i_sem); + phys = fs_get_block (inode, block, 1, &err, &created); + up(&inode->i_sem); + if (!phys) + goto out; + + init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); + bh->b_state = (1<<BH_Uptodate); + } else { + /* + * block already exists, just mark it uptodate and + * dirty: + */ + bh->b_end_io = end_buffer_io_sync; + set_bit(BH_Uptodate, &bh->b_state); + } + atomic_mark_buffer_dirty(bh,0); + + bh = bh->b_this_page; + block++; + } while (bh != head); + + SetPageUptodate(page); + return 0; +out: + ClearPageUptodate(page); + return err; +} + +int block_write_partial_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int err, created, partial; + unsigned long blocksize, start_block, end_block; + unsigned long start_offset, start_bytes, end_bytes; + unsigned long bbits, phys, blocks, i, len; + struct buffer_head *bh, *head; + char * target_buf; + + target_buf = (char *)page_address(page) + offset; + + if (!PageLocked(page)) + BUG(); + + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; + + bbits = inode->i_sb->s_blocksize_bits; + block = page->offset >> bbits; + blocks = PAGE_SIZE >> bbits; + start_block = offset >> bbits; + end_block = (offset + bytes - 1) >> bbits; + start_offset = offset & (blocksize - 1); + start_bytes = blocksize - start_offset; + if (start_bytes > bytes) + start_bytes = bytes; + end_bytes = (offset+bytes) & (blocksize - 1); + if (end_bytes > bytes) + end_bytes = bytes; + + if (offset < 0 || offset >= PAGE_SIZE) + BUG(); + if (bytes+offset < 0 || bytes+offset > PAGE_SIZE) + BUG(); + if (start_block < 0 || start_block >= blocks) + BUG(); + if (end_block < 0 || end_block >= blocks) + BUG(); + // FIXME: currently we assume page alignment. + if (page->offset & (PAGE_SIZE-1)) + BUG(); + + i = 0; + bh = head; + partial = 0; + do { + if (!bh) + BUG(); + + if ((i < start_block) || (i > end_block)) { + if (!buffer_uptodate(bh)) + partial = 1; + goto skip; + } + if (!bh->b_blocknr) { + err = -EIO; + down(&inode->i_sem); + phys = fs_get_block (inode, block, 1, &err, &created); + up(&inode->i_sem); + if (!phys) + goto out; + + init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); + + /* + * if partially written block which has contents on + * disk, then we have to read it first. + * We also rely on the fact that filesystem holes + * cannot be written. + */ + if (!created && (start_offset || + (end_bytes && (i == end_block)))) { + bh->b_state = 0; + ll_rw_block(READ, 1, &bh); + lock_kernel(); + wait_on_buffer(bh); + unlock_kernel(); + err = -EIO; + if (!buffer_uptodate(bh)) + goto out; + } + + bh->b_state = (1<<BH_Uptodate); + } else { + /* + * block already exists, just mark it uptodate: + */ + bh->b_end_io = end_buffer_io_sync; + set_bit(BH_Uptodate, &bh->b_state); + } + + err = -EFAULT; + if (start_offset) { + len = start_bytes; + start_offset = 0; + } else + if (end_bytes && (i == end_block)) { + len = end_bytes; + end_bytes = 0; + } else { + /* + * Overwritten block. + */ + len = blocksize; } - tmp = get_hash_table(dev, block, size); - if (tmp) { - if (!buffer_uptodate(tmp)) { - if (rw == READ) - ll_rw_block(READ, 1, &tmp); - wait_on_buffer(tmp); + if (copy_from_user(target_buf, buf, len)) + goto out; + target_buf += len; + buf += len; + + /* + * we dirty buffers only after copying the data into + * the page - this way we can dirty the buffer even if + * the bh is still doing IO. + */ + atomic_mark_buffer_dirty(bh,0); +skip: + i++; + block++; + bh = bh->b_this_page; + } while (bh != head); + + /* + * is this a partial write that happened to make all buffers + * uptodate then we can optimize away a bogus readpage() for + * the next read(). Here we 'discover' wether the page went + * uptodate as a result of this (potentially partial) write. + */ + if (!partial) + SetPageUptodate(page); + return bytes; +out: + ClearPageUptodate(page); + return err; +} + +/* + * Start I/O on a page. + * This function expects the page to be locked and may return + * before I/O is complete. You then have to check page->locked, + * page->uptodate, and maybe wait on page->wait. + * + * brw_page() is SMP-safe, although it's being called with the + * kernel lock held - but the code is ready. + */ +int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) +{ + struct buffer_head *head, *bh, *arr[MAX_BUF_PER_PAGE]; + int nr, fresh /* temporary debugging flag */, block; + + if (!PageLocked(page)) + panic("brw_page: page not locked for I/O"); +// clear_bit(PG_error, &page->flags); + /* + * We pretty much rely on the page lock for this, because + * create_page_buffers() might sleep. + */ + fresh = 0; + if (!page->buffers) { + create_page_buffers(rw, page, dev, b, size, bmap); + fresh = 1; + } + if (!page->buffers) + BUG(); + page->owner = -1; + + head = page->buffers; + bh = head; + nr = 0; + do { + block = *(b++); + + if (fresh && (bh->b_count != 0)) + BUG(); + if (rw == READ) { + if (!fresh) + BUG(); + if (bmap && !block) { + if (block) + BUG(); + } else { + if (bmap && !block) + BUG(); + if (!buffer_uptodate(bh)) { + arr[nr++] = bh; + } } - if (rw == READ) - memcpy(next->b_data, tmp->b_data, size); - else { - memcpy(tmp->b_data, next->b_data, size); - mark_buffer_dirty(tmp, 0); + } else { /* WRITE */ + if (!bh->b_blocknr) { + if (!block) + BUG(); + bh->b_blocknr = block; + } else { + if (!block) + BUG(); } - brelse(tmp); - next->b_count--; - continue; + set_bit(BH_Uptodate, &bh->b_state); + atomic_mark_buffer_dirty(bh, 0); + arr[nr++] = bh; } - if (rw == READ) - clear_bit(BH_Uptodate, &next->b_state); - else - set_bit(BH_Dirty, &next->b_state); - arr[nr++] = next; - } while (prev = next, (next = next->b_this_page) != NULL); - prev->b_this_page = bh; - - if (nr) { + bh = bh->b_this_page; + } while (bh != head); + if (rw == READ) + ++current->maj_flt; + if ((rw == READ) && nr) { + if (Page_Uptodate(page)) + BUG(); ll_rw_block(rw, nr, arr); - /* The rest of the work is done in mark_buffer_uptodate() - * and unlock_buffer(). */ } else { - unsigned long flags; - clear_bit(PG_locked, &page->flags); - set_bit(PG_uptodate, &page->flags); - wake_up(&page->wait); - save_flags(flags); - cli(); - free_async_buffers(bh); - restore_flags(flags); - after_unlock_page(page); + if (!nr && rw == READ) { + SetPageUptodate(page); + page->owner = (int)current; + UnlockPage(page); + } + if (nr && (rw == WRITE)) + ll_rw_block(rw, nr, arr); } - ++current->maj_flt; return 0; } @@ -1305,6 +1662,7 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on) { if (on) { struct buffer_head *tmp = bh; + struct page *page; set_bit(BH_Uptodate, &bh->b_state); /* If a page has buffers and all these buffers are uptodate, * then the page is uptodate. */ @@ -1313,7 +1671,8 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on) return; tmp=tmp->b_this_page; } while (tmp && tmp != bh); - set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags); + page = mem_map + MAP_NR(bh->b_data); + SetPageUptodate(page); return; } clear_bit(BH_Uptodate, &bh->b_state); @@ -1326,30 +1685,70 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on) * mark_buffer_uptodate() functions propagate buffer state into the * page struct once IO has completed. */ -int generic_readpage(struct file * file, struct page * page) +int block_read_full_page(struct file * file, struct page * page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - unsigned long block; - int *p, nr[PAGE_SIZE/512]; - int i; + unsigned long iblock, phys_block; + struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; + unsigned int blocksize, blocks; + int nr; - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); - set_bit(PG_free_after, &page->flags); - - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - block = page->offset >> inode->i_sb->s_blocksize_bits; - p = nr; + if (!PageLocked(page)) + PAGE_BUG(page); + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; + + blocks = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; + iblock = page->offset >> inode->i_sb->s_blocksize_bits; + page->owner = -1; + head = page->buffers; + bh = head; + nr = 0; do { - *p = inode->i_op->bmap(inode, block); - i--; - block++; - p++; - } while (i > 0); + phys_block = bh->b_blocknr; + /* + * important, we have to retry buffers that already have + * their bnr cached but had an IO error! + */ + if (!buffer_uptodate(bh)) { + phys_block = inode->i_op->bmap(inode, iblock); + /* + * this is safe to do because we hold the page lock: + */ + if (phys_block) { + init_buffer(bh, inode->i_dev, phys_block, + end_buffer_io_async, NULL); + arr[nr] = bh; + nr++; + } else { + /* + * filesystem 'hole' represents zero-contents: + */ + memset(bh->b_data, 0, blocksize); + set_bit(BH_Uptodate, &bh->b_state); + } + } + iblock++; + bh = bh->b_this_page; + } while (bh != head); - /* IO start */ - brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); + ++current->maj_flt; + if (nr) { + if (Page_Uptodate(page)) + BUG(); + ll_rw_block(READ, nr, arr); + } else { + /* + * all buffers are uptodate - we can set the page + * uptodate as well. + */ + SetPageUptodate(page); + page->owner = (int)current; + UnlockPage(page); + } return 0; } @@ -1392,7 +1791,6 @@ static int grow_buffers(int size) tmp->b_next_free = tmp; } insert_point = tmp; - ++nr_buffers; if (tmp->b_this_page) tmp = tmp->b_this_page; else @@ -1409,7 +1807,7 @@ static int grow_buffers(int size) * Can the buffer be thrown out? */ #define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected)) -#define buffer_busy(bh) ((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS)) +#define buffer_busy(bh) ((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS)) /* * try_to_free_buffers() checks if all the buffers on this particular page @@ -1418,9 +1816,9 @@ static int grow_buffers(int size) * Wake up bdflush() if this fails - if we're running low on memory due * to dirty buffers, we need to flush them out as quickly as possible. */ -int try_to_free_buffers(struct page * page_map) +int try_to_free_buffers(struct page * page) { - struct buffer_head * tmp, * bh = page_map->buffers; + struct buffer_head * tmp, * bh = page->buffers; tmp = bh; do { @@ -1429,8 +1827,6 @@ int try_to_free_buffers(struct page * page_map) tmp = tmp->b_this_page; if (!buffer_busy(p)) continue; - - wakeup_bdflush(0); return 0; } while (tmp != bh); @@ -1438,8 +1834,13 @@ int try_to_free_buffers(struct page * page_map) do { struct buffer_head * p = tmp; tmp = tmp->b_this_page; - nr_buffers--; - remove_from_queues(p); + + /* The buffer can be either on the regular queues or on the free list.. */ + if (p->b_dev == B_FREE) + remove_from_free_list(p); + else + remove_from_queues(p); + put_unused_buffer_head(p); } while (tmp != bh); @@ -1447,10 +1848,12 @@ int try_to_free_buffers(struct page * page_map) wake_up(&buffer_wait); /* And free the page */ - buffermem -= PAGE_SIZE; - page_map->buffers = NULL; - __free_page(page_map); - return 1; + page->buffers = NULL; + if (__free_page(page)) { + buffermem -= PAGE_SIZE; + return 1; + } + return 0; } /* ================== Debugging =================== */ @@ -1509,11 +1912,11 @@ void __init buffer_init(unsigned long memory_size) the heuristic from working with large databases and getting fsync times (ext2) manageable, is the following */ - memory_size >>= 20; + memory_size >>= 22; for (order = 5; (1UL << order) < memory_size; order++); /* try to allocate something until we get it or we're asking - for something that is really too small */ + for something that is really too small */ do { nr_hash = (1UL << order) * PAGE_SIZE / @@ -1521,6 +1924,7 @@ void __init buffer_init(unsigned long memory_size) hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); } while (hash_table == NULL && --order > 4); + printk("buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", nr_hash, order, (1UL<<order) * PAGE_SIZE); if (!hash_table) panic("Failed to allocate buffer hash table\n"); @@ -1565,11 +1969,11 @@ void wakeup_bdflush(int wait) { if (current == bdflush_tsk) return; - wake_up(&bdflush_wait); - if (wait) { + if (wait) run_task_queue(&tq_disk); + wake_up(&bdflush_wait); + if (wait) sleep_on(&bdflush_done); - } } @@ -1801,6 +2205,7 @@ int bdflush(void * unused) #endif bh->b_count--; next->b_count--; + wake_up(&buffer_wait); } } #ifdef DEBUG @@ -1818,9 +2223,14 @@ int bdflush(void * unused) run_task_queue(&tq_disk); wake_up(&bdflush_done); - /* If there are still a lot of dirty buffers around, skip the sleep - and flush some more */ - if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { + /* + * If there are still a lot of dirty buffers around, + * skip the sleep and flush some more + */ + if ((ndirty == 0) || (nr_buffers_type[BUF_DIRTY] <= + nr_buffers * bdf_prm.b_un.nfract/100)) { + + atomic_set(&too_many_dirty_buffers, 0); spin_lock_irq(¤t->sigmask_lock); flush_signals(current); spin_unlock_irq(¤t->sigmask_lock); diff --git a/fs/devices.c b/fs/devices.c index 8d9200f87..934fe290f 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -277,11 +277,14 @@ struct inode_operations blkdev_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff --git a/fs/devpts/root.c b/fs/devpts/root.c index c284f1d97..c1c1a6000 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -57,7 +57,6 @@ struct inode_operations devpts_root_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 2c7ba02d7..053022309 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -358,7 +358,7 @@ error_return: * bitmap, and then for any free bit if that fails. */ int ext2_new_block (const struct inode * inode, unsigned long goal, - u32 * prealloc_count, u32 * prealloc_block, int * err) + u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; struct buffer_head * bh2; @@ -594,20 +594,12 @@ got_block: if (j >= le32_to_cpu(es->s_blocks_count)) { ext2_error (sb, "ext2_new_block", - "block >= blocks count - " - "block_group = %d, block=%d", i, j); + "block(%d) >= blocks count(%d) - " + "block_group = %d, es == %p ",j, + le32_to_cpu(es->s_blocks_count), i, es); unlock_super (sb); return 0; } - if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { - ext2_error (sb, "ext2_new_block", "cannot get block %d", j); - unlock_super (sb); - return 0; - } - memset(bh->b_data, 0, sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); ext2_debug ("allocating block %d. " "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index a6753d276..59f068b5e 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -67,12 +67,14 @@ struct inode_operations ext2_dir_inode_operations = { ext2_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ ext2_permission, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; int ext2_check_dir_entry (const char * function, struct inode * dir, diff --git a/fs/ext2/file.c b/fs/ext2/file.c index e0f497057..c90419ce3 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -30,15 +30,15 @@ #include <linux/locks.h> #include <linux/mm.h> #include <linux/pagemap.h> +#include <linux/smp_lock.h> #define NBUF 32 #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +static int ext2_writepage (struct file * file, struct page * page); static long long ext2_file_lseek(struct file *, long long, int); -static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *); -static int ext2_release_file (struct inode *, struct file *); #if BITS_PER_LONG < 64 static int ext2_open_file (struct inode *, struct file *); @@ -57,51 +57,6 @@ EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13) #endif -/* - * We have mostly NULL's here: the current defaults are ok for - * the ext2 filesystem. - */ -static struct file_operations ext2_file_operations = { - ext2_file_lseek, /* lseek */ - generic_file_read, /* read */ - ext2_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - ext2_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ -#if BITS_PER_LONG == 64 - NULL, /* no special open is needed */ -#else - ext2_open_file, -#endif - NULL, /* flush */ - ext2_release_file, /* release */ - ext2_sync_file, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL /* revalidate */ -}; - -struct inode_operations ext2_file_inode_operations = { - &ext2_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ - ext2_bmap, /* bmap */ - ext2_truncate, /* truncate */ - ext2_permission, /* permission */ - NULL /* smap */ -}; /* * Make sure the offset never goes beyond the 32-bit mark.. @@ -151,164 +106,30 @@ static inline void remove_suid(struct inode *inode) } } -static ssize_t ext2_file_write (struct file * filp, const char * buf, - size_t count, loff_t *ppos) +static int ext2_writepage (struct file * file, struct page * page) { - struct inode * inode = filp->f_dentry->d_inode; - off_t pos; - long block; - int offset; - int written, c; - struct buffer_head * bh, *bufferlist[NBUF]; - struct super_block * sb; - int err; - int i,buffercount,write_error; - - /* POSIX: mtime/ctime may not change for 0 count */ - if (!count) - return 0; - write_error = buffercount = 0; - if (!inode) { - printk("ext2_file_write: inode = NULL\n"); - return -EINVAL; - } - sb = inode->i_sb; - if (sb->s_flags & MS_RDONLY) - /* - * This fs has been automatically remounted ro because of errors - */ - return -ENOSPC; - - if (!S_ISREG(inode->i_mode)) { - ext2_warning (sb, "ext2_file_write", "mode = %07o", - (unsigned int) inode->i_mode); - return -EINVAL; - } - remove_suid(inode); - - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else { - pos = *ppos; - if (pos != *ppos) - return -EINVAL; -#if BITS_PER_LONG >= 64 - if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]) - return -EINVAL; -#endif - } + return block_write_full_page(file, page, ext2_getblk_block); +} - /* Check for overflow.. */ -#if BITS_PER_LONG < 64 - if (pos > (__u32) (pos + count)) { - count = ~pos; /* == 0xFFFFFFFF - pos */ - if (!count) - return -EFBIG; - } -#else - { - off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]; +static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_partial_page(file, page, offset, bytes, buf, ext2_getblk_block); +} - if (pos + count > max) { - count = max - pos; - if (!count) - return -EFBIG; - } - if (((pos + count) >> 32) && - !(sb->u.ext2_sb.s_es->s_feature_ro_compat & - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { - /* If this is the first large file created, add a flag - to the superblock */ - sb->u.ext2_sb.s_es->s_feature_ro_compat |= - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - } +/* + * Write to a file (through the page cache). + */ +static ssize_t +ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + remove_suid(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); } -#endif - - /* - * If a file has been opened in synchronous mode, we have to ensure - * that meta-data will also be written synchronously. Thus, we - * set the i_osync field. This field is tested by the allocation - * routines. - */ - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync++; - block = pos >> EXT2_BLOCK_SIZE_BITS(sb); - offset = pos & (sb->s_blocksize - 1); - c = sb->s_blocksize - offset; - written = 0; - do { - bh = ext2_getblk (inode, block, 1, &err); - if (!bh) { - if (!written) - written = err; - break; - } - if (c > count) - c = count; - if (c != sb->s_blocksize && !buffer_uptodate(bh)) { - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - if (!written) - written = -EIO; - break; - } - } - c -= copy_from_user (bh->b_data + offset, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, bh->b_data + offset, c); - pos += c; - written += c; - buf += c; - count -= c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - - if (filp->f_flags & O_SYNC) - bufferlist[buffercount++] = bh; - else - brelse(bh); - if (buffercount == NBUF){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - buffercount=0; - } - if(write_error) - break; - block++; - offset = 0; - c = sb->s_blocksize; - } while (count); - if ( buffercount ){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - } - if (pos > inode->i_size) - inode->i_size = pos; - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync--; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; + return retval; } /* @@ -335,3 +156,51 @@ static int ext2_open_file (struct inode * inode, struct file * filp) return 0; } #endif + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ext2 filesystem. + */ +static struct file_operations ext2_file_operations = { + ext2_file_lseek, /* lseek */ + generic_file_read, /* read */ + ext2_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + ext2_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 + NULL, /* no special open is needed */ +#else + ext2_open_file, +#endif + NULL, /* flush */ + ext2_release_file, /* release */ + ext2_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ext2_file_inode_operations = { + &ext2_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext2_bmap, /* bmap */ + block_read_full_page, /* readpage */ + ext2_writepage, /* writepage */ + block_flushpage, /* flushpage */ + ext2_truncate, /* truncate */ + ext2_permission, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ +}; diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 1a13c876b..8ae361e73 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -17,6 +17,9 @@ * Removed unnecessary code duplication for little endian machines * and excessive __inline__s. * Andi Kleen, 1997 + * + * Major simplications and cleanup - we only need to do the metadata, because + * we can depend on generic_block_fdatasync() to sync the data blocks. */ #include <asm/uaccess.h> @@ -32,221 +35,84 @@ #include <linux/locks.h> -#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) -#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) +#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) +#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) -static int sync_block (struct inode * inode, u32 * block, int wait) +static int sync_indirect(struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; if (!*block) return 0; - bh = get_hash_table (inode->i_dev, *block, blocksize); + bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize); if (!bh) return 0; if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); + brelse(bh); return -1; } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); + brelse(bh); return 0; } - ll_rw_block (WRITE, 1, &bh); + ll_rw_block(WRITE, 1, &bh); bh->b_count--; return 0; } -#ifndef __LITTLE_ENDIAN -static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!le32_to_cpu(*block)) - return 0; - bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} -#else -#define sync_block_swab32 sync_block -#endif - - -static int sync_iblock (struct inode * inode, u32 * iblock, +static int sync_iblock(struct inode * inode, u32 * iblock, struct buffer_head ** bh, int wait) { int rc, tmp; *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; tmp = le32_to_cpu(*iblock); if (!tmp) return 0; - rc = sync_block_swab32 (inode, iblock, wait); + rc = sync_indirect(inode, iblock, wait); if (rc) return rc; - *bh = bread (inode->i_dev, tmp, blocksize); + *bh = bread(inode->i_dev, tmp, blocksize); if (!*bh) return -1; return 0; } -#else -#define sync_iblock_swab32 sync_iblock -#endif - -static int sync_direct (struct inode * inode, int wait) -{ - int i; - int rc, err = 0; - - for (i = 0; i < EXT2_NDIR_BLOCKS; i++) { - rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait); - if (rc) - err = rc; - } - return err; -} - -static int sync_indirect (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} -#else -#define sync_indirect_swab32 sync_indirect -#endif -static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) +static int sync_dindirect(struct inode * inode, u32 * diblock, int wait) { int i; struct buffer_head * dind_bh; int rc, err = 0; - rc = sync_iblock (inode, diblock, &dind_bh, wait); + rc = sync_iblock(inode, diblock, &dind_bh, wait); if (rc || !dind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); + rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (dind_bh); + brelse(dind_bh); return err; } -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} -#else -#define sync_dindirect_swab32 sync_dindirect -#endif - -static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) +static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait) { int i; struct buffer_head * tind_bh; int rc, err = 0; - rc = sync_iblock (inode, tiblock, &tind_bh, wait); + rc = sync_iblock(inode, tiblock, &tind_bh, wait); if (rc || !tind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_dindirect_swab32 (inode, - ((u32 *) tind_bh->b_data) + i, - wait); + rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (tind_bh); + brelse(tind_bh); return err; } @@ -266,18 +132,19 @@ int ext2_sync_file(struct file * file, struct dentry *dentry) */ goto skip; + err = generic_buffer_fdatasync(inode, 0, ~0UL); + for (wait=0; wait<=1; wait++) { - err |= sync_direct (inode, wait); - err |= sync_indirect (inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + err |= sync_indirect(inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + wait); + err |= sync_dindirect(inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect(inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, wait); - err |= sync_dindirect (inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect (inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, - wait); } skip: err |= ext2_sync_inode (inode); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 693964a80..02fb5b7b7 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,6 +31,7 @@ #include <linux/string.h> #include <linux/locks.h> #include <linux/mm.h> +#include <linux/smp_lock.h> static int ext2_update_inode(struct inode * inode, int do_sync); @@ -59,7 +60,7 @@ void ext2_delete_inode (struct inode * inode) ext2_free_inode (inode); } -#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)]) +#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])) static inline int block_bmap (struct buffer_head * bh, int nr) { @@ -92,13 +93,12 @@ void ext2_discard_prealloc (struct inode * inode) #endif } -static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err) +static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) { #ifdef EXT2FS_DEBUG static unsigned long alloc_hits = 0, alloc_attempts = 0; #endif unsigned long result; - struct buffer_head * bh; wait_on_super (inode->i_sb); @@ -112,19 +112,6 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); - /* It doesn't matter if we block in getblk() since - we have already atomically allocated the block, and - are only clearing it now. */ - if (!(bh = getblk (inode->i_sb->s_dev, result, - inode->i_sb->s_blocksize))) { - ext2_error (inode->i_sb, "ext2_alloc_block", - "cannot get block %lu", result); - return 0; - } - memset(bh->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); } else { ext2_discard_prealloc (inode); ext2_debug ("preallocation miss (%lu/%lu).\n", @@ -139,13 +126,76 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err #else result = ext2_new_block (inode, goal, 0, 0, err); #endif - return result; } int ext2_bmap (struct inode * inode, int block) { + int i, ret; + int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + + ret = 0; + lock_kernel(); + if (block < 0) { + ext2_warning (inode->i_sb, "ext2_bmap", "block < 0"); + goto out; + } + if (block >= EXT2_NDIR_BLOCKS + addr_per_block + + (1 << (addr_per_block_bits * 2)) + + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { + ext2_warning (inode->i_sb, "ext2_bmap", "block > big"); + goto out; + } + if (block < EXT2_NDIR_BLOCKS) { + ret = inode_bmap (inode, block); + goto out; + } + block -= EXT2_NDIR_BLOCKS; + if (block < addr_per_block) { + i = inode_bmap (inode, EXT2_IND_BLOCK); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), block); + goto out; + } + block -= addr_per_block; + if (block < (1 << (addr_per_block_bits * 2))) { + i = inode_bmap (inode, EXT2_DIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block >> addr_per_block_bits); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); + } + block -= (1 << (addr_per_block_bits * 2)); + i = inode_bmap (inode, EXT2_TIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block >> (addr_per_block_bits * 2)); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + (block >> addr_per_block_bits) & (addr_per_block - 1)); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); +out: + unlock_kernel(); + return ret; +} + +int ext2_bmap_create (struct inode * inode, int block) +{ int i; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); @@ -201,7 +251,8 @@ int ext2_bmap (struct inode * inode, int block) } static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create, int new_block, int * err) + int create, int new_block, int * err, int metadata, + int *phys_block, int *created) { u32 * p; int tmp, goal = 0; @@ -210,13 +261,18 @@ static struct buffer_head * inode_getblk (struct inode * inode, int nr, p = inode->u.ext2_i.i_data + nr; repeat: - tmp = *p; + tmp = le32_to_cpu(*p); if (tmp) { - struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp == *p) - return result; - brelse (result); - goto repeat; + if (metadata) { + struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp == le32_to_cpu(*p)) + return result; + brelse (result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } *err = -EFBIG; if (!create) @@ -244,7 +300,7 @@ dont_create: if (!goal) { for (tmp = nr - 1; tmp >= 0; tmp--) { if (inode->u.ext2_i.i_data[tmp]) { - goal = inode->u.ext2_i.i_data[tmp]; + goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]); break; } } @@ -259,13 +315,28 @@ dont_create: tmp = ext2_alloc_block (inode, goal, err); if (!tmp) return NULL; - result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (*p) { - ext2_free_blocks (inode, tmp, 1); - brelse (result); - goto repeat; + if (metadata) { + result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + ext2_free_blocks (inode, tmp, 1); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; } - *p = tmp; + *p = cpu_to_le32(tmp); + inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; @@ -277,10 +348,17 @@ dont_create: return result; } +/* + * metadata / data + * possibly create / access + * can fail due to: - not present + * - out of space + * + * NULL return in the data case is mandatory. + */ static struct buffer_head * block_getblk (struct inode * inode, - struct buffer_head * bh, int nr, - int create, int blocksize, - int new_block, int * err) + struct buffer_head * bh, int nr, int create, int blocksize, + int new_block, int * err, int metadata, int *phys_block, int *created) { int tmp, goal = 0; u32 * p; @@ -302,13 +380,19 @@ static struct buffer_head * block_getblk (struct inode * inode, repeat: tmp = le32_to_cpu(*p); if (tmp) { - result = getblk (bh->b_dev, tmp, blocksize); - if (tmp == le32_to_cpu(*p)) { + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (tmp == le32_to_cpu(*p)) { + brelse (bh); + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; brelse (bh); - return result; + return NULL; } - brelse (result); - goto repeat; } *err = -EFBIG; if (!create) { @@ -343,7 +427,22 @@ repeat: brelse (bh); return NULL; } - result = getblk (bh->b_dev, tmp, blocksize); + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } if (le32_to_cpu(*p)) { ext2_free_blocks (inode, tmp, 1); brelse (result); @@ -364,24 +463,27 @@ repeat: return result; } -struct buffer_head * ext2_getblk (struct inode * inode, long block, - int create, int * err) +int ext2_getblk_block (struct inode * inode, long block, + int create, int * err, int * created) { - struct buffer_head * bh; + struct buffer_head * bh, *tmp; unsigned long b; unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + int phys_block, ret; + lock_kernel(); + ret = 0; *err = -EIO; if (block < 0) { ext2_warning (inode->i_sb, "ext2_getblk", "block < 0"); - return NULL; + goto abort; } if (block > EXT2_NDIR_BLOCKS + addr_per_block + (1 << (addr_per_block_bits * 2)) + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { ext2_warning (inode->i_sb, "ext2_getblk", "block > big"); - return NULL; + goto abort; } /* * If this is a sequential block allocation, set the next_alloc_block @@ -398,32 +500,72 @@ struct buffer_head * ext2_getblk (struct inode * inode, long block, inode->u.ext2_i.i_next_alloc_goal++; } - *err = -ENOSPC; + *err = 0; // -ENOSPC; b = block; - if (block < EXT2_NDIR_BLOCKS) - return inode_getblk (inode, block, create, b, err); + *created = 0; + if (block < EXT2_NDIR_BLOCKS) { + /* + * data page. + */ + tmp = inode_getblk (inode, block, create, b, + err, 0, &phys_block, created); + goto out; + } block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err); - return block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize, b, err); + bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block, create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= addr_per_block; if (block < (1 << (addr_per_block_bits * 2))) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL); bh = block_getblk (inode, bh, block >> addr_per_block_bits, - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= (1 << (addr_per_block_bits * 2)); - bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL); bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2), - create, inode->i_sb->s_blocksize, b, err); - bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL); + bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & + (addr_per_block - 1), create, inode->i_sb->s_blocksize, + b, err, 1, NULL,NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + +out: + if (!phys_block) + goto abort; + if (*err) + goto abort; + ret = phys_block; +abort: + unlock_kernel(); + return ret; +} + +struct buffer_head * ext2_getblk (struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int created; + + phys_block = ext2_getblk_block (inode, block, create, err, &created); + + if (phys_block) { + tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize); + if (created) { + memset(tmp->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; } struct buffer_head * ext2_bread (struct inode * inode, int block, @@ -569,11 +711,14 @@ void ext2_read_inode (struct inode * inode) if (inode->u.ext2_i.i_prealloc_count) ext2_error (inode->i_sb, "ext2_read_inode", "New inode has non-zero prealloc count!"); - if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]); + + /* + * NOTE! The in-memory inode i_blocks array is in little-endian order + * even on big-endian machines: we do NOT byteswap the block numbers! + */ + for (block = 0; block < EXT2_N_BLOCKS; block++) + inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; @@ -689,11 +834,8 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); - else if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]); + raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; mark_buffer_dirty(bh, 1); if (do_sync) { ll_rw_block (WRITE, 1, &bh); diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 826cb4176..b0ebcb91b 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -43,12 +43,14 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* rename */ ext2_readlink, /* readlink */ ext2_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * ext2_follow_link(struct dentry * dentry, diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index 84eacf87d..75ffaa534 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -131,10 +131,7 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh, if (bh->b_count == 1) { int tmp; - if (ind_bh) - tmp = le32_to_cpu(*p); - else - tmp = *p; + tmp = le32_to_cpu(*p); *p = 0; inode->i_blocks -= (inode->i_sb->s_blocksize / 512); mark_inode_dirty(inode); @@ -160,6 +157,9 @@ out: return retry; } +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + static int trunc_direct (struct inode * inode) { struct buffer_head * bh; @@ -170,7 +170,7 @@ static int trunc_direct (struct inode * inode) for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) { u32 * p = inode->u.ext2_i.i_data + i; - int tmp = *p; + int tmp = le32_to_cpu(*p); if (!tmp) continue; @@ -178,7 +178,7 @@ static int trunc_direct (struct inode * inode) bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if(bh->b_count != 1 || buffer_locked(bh)) { + if (DATA_BUFFER_USED(bh)) { brelse(bh); retry = 1; continue; @@ -215,11 +215,11 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, unsigned long block_to_free = 0, free_count = 0; int indirect_block, addr_per_block, blocks; - tmp = dind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (ind_bh); return 1; } @@ -255,8 +255,8 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if (bh->b_count != 1 || buffer_locked(bh)) { - brelse (bh); + if (DATA_BUFFER_USED(bh)) { + brelse(bh); retry = 1; continue; } @@ -297,11 +297,11 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p, int i, tmp, retry = 0; int dindirect_block, addr_per_block; - tmp = tind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (dind_bh); return 1; } @@ -344,10 +344,11 @@ static int trunc_tindirect (struct inode * inode) int i, tmp, retry = 0; int tindirect_block, addr_per_block, offset; - if (!(tmp = *p)) + tmp = le32_to_cpu(*p); + if (!tmp) return 0; tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { + if (tmp != le32_to_cpu(*p)) { brelse (tind_bh); return 1; } @@ -384,8 +385,6 @@ static int trunc_tindirect (struct inode * inode) void ext2_truncate (struct inode * inode) { - int err, offset; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; @@ -411,25 +410,6 @@ void ext2_truncate (struct inode * inode) current->policy |= SCHED_YIELD; schedule(); } - /* - * If the file is not being truncated to a block boundary, the - * contents of the partial block following the end of the file - * must be zeroed in case it ever becomes accessible again due - * to subsequent file growth. - */ - offset = inode->i_size & (inode->i_sb->s_blocksize - 1); - if (offset) { - struct buffer_head * bh; - bh = ext2_bread (inode, - inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb), - 0, &err); - if (bh) { - memset (bh->b_data + offset, 0, - inode->i_sb->s_blocksize - offset); - mark_buffer_dirty (bh, 0); - brelse (bh); - } - } inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } @@ -2,14 +2,45 @@ * linux/fs/fifo.c * * written by Paul H. Hargrove + * + * Fixes: + * 10-06-1999, AV: fixed OOM handling in fifo_open(), moved + * initialization there, switched to external + * allocation of pipe_inode_info. */ #include <linux/mm.h> +#include <linux/malloc.h> static int fifo_open(struct inode * inode,struct file * filp) { int retval = 0; - unsigned long page; + unsigned long page = 0; + struct pipe_inode_info *info, *tmp = NULL; + + if (inode->i_pipe) + goto got_it; + tmp = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL); + if (inode->i_pipe) + goto got_it; + if (!tmp) + goto oom; + page = __get_free_page(GFP_KERNEL); + if (inode->i_pipe) + goto got_it; + if (!page) + goto oom; + inode->i_pipe = tmp; + PIPE_LOCK(*inode) = 0; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_BASE(*inode) = (char *) page; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + init_waitqueue_head(&PIPE_WAIT(*inode)); + tmp = NULL; /* no need to free it */ + page = 0; + +got_it: switch( filp->f_mode ) { @@ -94,19 +125,26 @@ static int fifo_open(struct inode * inode,struct file * filp) default: retval = -EINVAL; } - if (retval || PIPE_BASE(*inode)) - return retval; - page = __get_free_page(GFP_KERNEL); - if (PIPE_BASE(*inode)) { + if (retval) + goto cleanup; +out: + if (tmp) + kfree(tmp); + if (page) free_page(page); - return 0; + return retval; + +cleanup: + if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { + info = inode->i_pipe; + inode->i_pipe = NULL; + free_page((unsigned long)info->base); + kfree(info); } - if (!page) - return -ENOMEM; - PIPE_LOCK(*inode) = 0; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_BASE(*inode) = (char *) page; - return 0; + goto out; +oom: + retval = -ENOMEM; + goto out; } /* @@ -141,20 +179,20 @@ struct inode_operations fifo_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; + +/* Goner. Filesystems do not use it anymore. */ + void init_fifo(struct inode * inode) { inode->i_op = &fifo_inode_operations; - PIPE_LOCK(*inode) = 0; - PIPE_BASE(*inode) = NULL; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - init_waitqueue_head(&PIPE_WAIT(*inode)); - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; } diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index be6974b66..21d4ca9af 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -99,7 +99,6 @@ struct inode_operations hfs_nat_ndir_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; @@ -122,7 +121,6 @@ struct inode_operations hfs_nat_hdir_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/hfs/file.c b/fs/hfs/file.c index 00bebd017..d3796e275 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -69,7 +69,6 @@ struct inode_operations hfs_file_inode_operations = { hfs_file_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index b3a58912c..789073d19 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -83,7 +83,6 @@ struct inode_operations hfs_cap_info_inode_operations = { cap_info_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidata */ }; diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index c1e1534b0..d112b3498 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -85,7 +85,6 @@ struct inode_operations hfs_hdr_inode_operations = { hdr_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 99bfa1004..17984d667 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -48,7 +48,6 @@ static const struct inode_operations hpfs_file_iops = &hpfs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -91,7 +90,6 @@ static const struct inode_operations hpfs_dir_iops = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -115,7 +113,6 @@ const struct inode_operations hpfs_symlink_iops = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff --git a/fs/inode.c b/fs/inode.c index 88805efe6..ba9cc7a78 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -130,7 +130,6 @@ static inline void init_once(struct inode * inode) INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); - sema_init(&inode->i_atomic_write, 1); } static inline void write_inode(struct inode *inode) @@ -337,7 +336,7 @@ int invalidate_inodes(struct super_block * sb) * dispose_list. */ #define CAN_UNUSE(inode) \ - (((inode)->i_count | (inode)->i_state) == 0) + (((inode)->i_count | (inode)->i_state | (inode)->i_nrpages) == 0) #define INODE(entry) (list_entry(entry, struct inode, i_list)) static int free_inodes(void) @@ -527,6 +526,7 @@ void clean_inode(struct inode *inode) inode->i_generation = 0; memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); sema_init(&inode->i_sem, 1); + inode->i_pipe = NULL; } /* @@ -766,9 +766,6 @@ kdevname(inode->i_dev), inode->i_ino, inode->i_count); if (atomic_read(&inode->i_sem.count) != 1) printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n", kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); -if (atomic_read(&inode->i_atomic_write.count) != 1) -printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n", -kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); #endif } if (inode->i_count > (1<<31)) { diff --git a/fs/isofs/file.c b/fs/isofs/file.c index e2b4405d9..ce85b367a 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -48,9 +48,10 @@ struct inode_operations isofs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ isofs_bmap, /* bmap */ + block_read_full_page, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ NULL, /* truncate */ NULL /* permission */ }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 1d88aaea8..01d37a849 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/nls.h> #include <linux/ctype.h> +#include <linux/smp_lock.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -909,7 +910,7 @@ int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } -int isofs_bmap(struct inode * inode,int block) +static int do_isofs_bmap(struct inode * inode,int block) { off_t b_off, offset, size; struct inode *ino; @@ -991,6 +992,15 @@ int isofs_bmap(struct inode * inode,int block) return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); } +int isofs_bmap(struct inode * inode,int block) +{ + int retval; + + lock_kernel(); + retval = do_isofs_bmap(inode, block); + unlock_kernel(); + return retval; +} static void test_and_set_uid(uid_t *p, uid_t value) { diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 6e8930c70..8c396f3e6 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -112,14 +112,6 @@ repeat: if (j < sb->u.minix_sb.s_firstdatazone || j >= sb->u.minix_sb.s_nzones) return 0; - if (!(bh = getblk(sb->s_dev,j,BLOCK_SIZE))) { - printk("new_block: cannot get block"); - return 0; - } - memset(bh->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse(bh); return j; } diff --git a/fs/minix/file.c b/fs/minix/file.c index f6ddda021..d6b7ecb17 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -27,7 +27,51 @@ #include <linux/fs.h> #include <linux/minix_fs.h> -static ssize_t minix_file_write(struct file *, const char *, size_t, loff_t *); +static int minix_writepage(struct file *file, struct page *page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/BLOCK_SIZE]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE / BLOCK_SIZE; + block = page->offset / BLOCK_SIZE; + p = nr; + bh = page->buffers; + do { + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; + else + *p = minix_getblk_block(inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; + block++; + p++; + if (bh) + bh = bh->b_this_page; + } while(i > 0); + + /* IO start */ + brw_page(WRITE, page, inode->i_dev, nr, BLOCK_SIZE, 1); + return 0; +} + +static long minix_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, minix_getblk_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +minix_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, minix_write_one_page); +} /* * We have mostly NULLs here: the current defaults are OK for @@ -61,74 +105,11 @@ struct inode_operations minix_file_inode_operations = { NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ - NULL, /* writepage */ + minix_writepage, /* writepage */ minix_bmap, /* bmap */ minix_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ }; - -static ssize_t minix_file_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - off_t pos; - ssize_t written, c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("minix_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("minix_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - written = 0; - while (written < count) { - bh = minix_getblk(inode,pos/BLOCK_SIZE,1); - if (!bh) { - if (!written) - written = -ENOSPC; - break; - } - c = BLOCK_SIZE - (pos % BLOCK_SIZE); - if (c > count-written) - c = count-written; - if (c != BLOCK_SIZE && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) - written = -EIO; - break; - } - } - p = (pos % BLOCK_SIZE) + bh->b_data; - c -= copy_from_user(p,buf,c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, p, c); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - pos += c; - written += c; - buf += c; - } - if (pos > inode->i_size) - inode->i_size = pos; - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; -} diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 5a29c53e0..088de42dc 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -407,7 +407,7 @@ static int V2_block_bmap(struct buffer_head * bh, int nr) return tmp; } -static int V2_minix_bmap(struct inode * inode,int block) +static int V2_minix_bmap(struct inode * inode, int block) { int i; @@ -454,7 +454,7 @@ static int V2_minix_bmap(struct inode * inode,int block) /* * The global minix fs bmap function. */ -int minix_bmap(struct inode * inode,int block) +int minix_bmap(struct inode * inode, int block) { if (INODE_VERSION(inode) == MINIX_V1) return V1_minix_bmap(inode, block); @@ -465,8 +465,8 @@ int minix_bmap(struct inode * inode,int block) /* * The minix V1 fs getblk functions. */ -static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, - int create) +static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned short *p; @@ -476,31 +476,51 @@ static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, repeat: tmp = *p; if (tmp) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = minix_new_block(inode->i_sb); if (!tmp) return NULL; - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * V1_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned short *p; @@ -520,13 +540,19 @@ static struct buffer_head * V1_block_getblk(struct inode * inode, repeat: tmp = *p; if (tmp) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -537,49 +563,74 @@ repeat: brelse(bh); return NULL; } - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } + *p = tmp; mark_buffer_dirty(bh, 1); brelse(bh); return result; } -static struct buffer_head * V1_minix_getblk(struct inode * inode, int block, - int create) +int V1_getblk_block(struct inode * inode, long block, int create, int *err, int *created) { - struct buffer_head * bh; + struct buffer_head *bh, *tmp; + int phys_block; - if (block<0) { + *err = -EIO; + if (block < 0) { printk("minix_getblk: block<0"); - return NULL; + return 0; } if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { printk("minix_getblk: block>big"); - return NULL; + return 0; + } + *created = 0; + if (block < 7) { + tmp = V1_inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; } - if (block < 7) - return V1_inode_getblk(inode,block,create); block -= 7; if (block < 512) { - bh = V1_inode_getblk(inode,7,create); - return V1_block_getblk(inode, bh, block, create); + bh = V1_inode_getblk(inode, 7, create, 1, NULL, NULL); + tmp = V1_block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= 512; - bh = V1_inode_getblk(inode,8,create); - bh = V1_block_getblk(inode, bh, (block>>9) & 511, create); - return V1_block_getblk(inode, bh, block & 511, create); + bh = V1_inode_getblk(inode, 8, create, 1, NULL, NULL); + bh = V1_block_getblk(inode, bh, (block>>9) & 511, create, 1, NULL, NULL); + tmp = V1_block_getblk(inode, bh, block & 511, create, 0, &phys_block, created); + +out: + *err = 0; + return phys_block; } /* * The minix V2 fs getblk functions. */ -static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, - int create) +static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned long *p; @@ -589,31 +640,51 @@ static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, repeat: tmp = *p; if (tmp) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = minix_new_block(inode->i_sb); if (!tmp) return NULL; - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * V2_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned long *p; @@ -633,13 +704,19 @@ static struct buffer_head * V2_block_getblk(struct inode * inode, repeat: tmp = *p; if (tmp) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -650,60 +727,107 @@ repeat: brelse(bh); return NULL; } - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } + *p = tmp; mark_buffer_dirty(bh, 1); brelse(bh); return result; } -static struct buffer_head * V2_minix_getblk(struct inode * inode, int block, - int create) +int V2_getblk_block(struct inode * inode, int block, int create, int *err, int *created) { - struct buffer_head * bh; + struct buffer_head * bh, *tmp; + int phys_block; - if (block<0) { + *err = -EIO; + if (block < 0) { printk("minix_getblk: block<0"); - return NULL; + return 0; } if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { printk("minix_getblk: block>big"); - return NULL; + return 0; + } + *created = 0; + if (block < 7) { + tmp = V2_inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; } - if (block < 7) - return V2_inode_getblk(inode,block,create); block -= 7; if (block < 256) { - bh = V2_inode_getblk(inode,7,create); - return V2_block_getblk(inode, bh, block, create); + bh = V2_inode_getblk(inode, 7, create, 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= 256; if (block < 256*256) { - bh = V2_inode_getblk(inode,8,create); - bh = V2_block_getblk(inode, bh, (block>>8) & 255, create); - return V2_block_getblk(inode, bh, block & 255, create); + bh = V2_inode_getblk(inode, 8, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block>>8) & 255, create, + 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block & 255, create, + 0, &phys_block, created); + goto out; } block -= 256*256; - bh = V2_inode_getblk(inode,9,create); - bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create); - bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create); - return V2_block_getblk(inode, bh, block & 255, create); + bh = V2_inode_getblk(inode, 9, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create, 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block & 255, create, 0, &phys_block, created); + +out: + *err = 0; + return phys_block; +} + +int minix_getblk_block (struct inode *inode, long block, + int create, int *err, int *created) +{ + if (INODE_VERSION(inode) == MINIX_V1) + return V1_getblk_block(inode, block, create, err, created); + else + return V2_getblk_block(inode, block, create, err, created); } /* * the global minix fs getblk function. */ -struct buffer_head * minix_getblk(struct inode * inode, int block, int create) +struct buffer_head *minix_getblk (struct inode *inode, int block, int create) { - if (INODE_VERSION(inode) == MINIX_V1) - return V1_minix_getblk(inode,block,create); - else - return V2_minix_getblk(inode,block,create); + struct buffer_head *tmp = NULL; + int phys_block; + int err, created; + + phys_block = minix_getblk_block(inode, block, create, &err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); + if (created) { + memset(tmp->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; } struct buffer_head * minix_bread(struct inode * inode, int block, int create) diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index a94806fdf..4718e092e 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -32,6 +32,9 @@ * general case (size = XXX). I hope. */ +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + /* * The functions for minix V1 fs truncation. */ @@ -52,7 +55,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *p) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { retry = 1; brelse(bh); continue; @@ -103,7 +106,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *ind) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { retry = 1; brelse(bh); continue; @@ -216,7 +219,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *p) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { retry = 1; brelse(bh); continue; @@ -267,7 +270,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *ind) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { retry = 1; brelse(bh); continue; diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index be1c34dac..8175724d7 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -633,7 +633,6 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 0aa50559b..680f011a1 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -98,7 +98,6 @@ struct inode_operations ncp_dir_inode_operations = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ed8b1fe0e..d41505862 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -78,13 +78,13 @@ struct inode_operations nfs_dir_inode_operations = { nfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ nfs_revalidate, /* revalidate */ }; @@ -118,6 +118,61 @@ struct nfs_cookie_table { }; static kmem_cache_t *nfs_cookie_cachep; +/* This whole scheme relies on the fact that dirent cookies + * are monotonically increasing. + * + * Another invariant is that once we have a valid non-zero + * EOF marker cached, we also have the complete set of cookie + * table entries. + * + * We return the page offset assosciated with the page where + * cookie must be if it exists at all, however if we can not + * figure that out conclusively, we return < 0. + */ +static long __nfs_readdir_offset(struct inode *inode, __u32 cookie) +{ + struct nfs_cookie_table *p; + unsigned long ret = 0; + + for(p = NFS_COOKIES(inode); p != NULL; p = p->next) { + int i; + + for (i = 0; i < COOKIES_PER_CHUNK; i++) { + __u32 this_cookie = p->cookies[i]; + + /* End of known cookies, EOF is our only hope. */ + if (!this_cookie) + goto check_eof; + + /* Next cookie is larger, must be in previous page. */ + if (this_cookie > cookie) + return ret; + + ret += 1; + + /* Exact cookie match, it must be in this page :-) */ + if (this_cookie == cookie) + return ret; + } + } +check_eof: + if (NFS_DIREOF(inode) != 0) + return ret; + + return -1L; +} + +static __inline__ long nfs_readdir_offset(struct inode *inode, __u32 cookie) +{ + /* Cookie zero is always at page offset zero. Optimize the + * other common case since most directories fit entirely + * in one page. + */ + if (!cookie || (!NFS_COOKIES(inode) && NFS_DIREOF(inode))) + return 0; + return __nfs_readdir_offset(inode, cookie); +} + /* Since a cookie of zero is declared special by the NFS * protocol, we easily can tell if a cookie in an existing * table chunk is valid or not. @@ -148,38 +203,7 @@ static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off) return ret; } -/* Now we cache directories properly, by stuffing the dirent - * data directly in the page cache. - * - * Inode invalidation due to refresh etc. takes care of - * _everything_, no sloppy entry flushing logic, no extraneous - * copying, network direct to page cache, the way it was meant - * to be. - * - * NOTE: Dirent information verification is done always by the - * page-in of the RPC reply, nowhere else, this simplies - * things substantially. - */ #define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2) -static u32 find_midpoint(__u32 *p, u32 doff) -{ - u32 walk = doff & PAGE_MASK; - - while(*p++ != 0) { - __u32 skip; - - p++; /* skip fileid */ - - /* Skip len, name, and cookie. */ - skip = NFS_NAMELEN_ALIGN(*p++); - p += (skip >> 2) + 1; - walk += skip + (4 * sizeof(__u32)); - if (walk >= doff) - break; - } - return walk; -} - static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode) { struct nfs_cookie_table **cpp; @@ -211,48 +235,74 @@ static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode) return 0; } -static struct page *try_to_get_dirent_page(struct file *, unsigned long, int); +static struct page *try_to_get_dirent_page(struct file *, __u32, int); /* Recover from a revalidation flush. The case here is that * the inode for the directory got invalidated somehow, and * all of our cached information is lost. In order to get * a correct cookie for the current readdir request from the * user, we must (re-)fetch older readdir page cache entries. + * + * Returns < 0 if some error occurrs, else it is the page offset + * to fetch. */ -static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off) +static long refetch_to_readdir_cookie(struct file *file, struct inode *inode) { - u32 cur_off, goal_off = off & PAGE_MASK; + struct page *page; + u32 goal_cookie = file->f_pos; + long cur_off, ret = -1L; again: cur_off = 0; - while (cur_off < goal_off) { - struct page *page; - - page = find_page(inode, cur_off); + for (;;) { + page = find_get_page(inode, cur_off); if (page) { - if (PageLocked(page)) - __wait_on_page(page); - if (!PageUptodate(page)) - return -1; + if (!Page_Uptodate(page)) + goto out_error; } else { - page = try_to_get_dirent_page(file, cur_off, 0); + __u32 *cp = find_cookie(inode, cur_off); + + if (!cp) + goto out_error; + + page = try_to_get_dirent_page(file, *cp, 0); if (!page) { if (!cur_off) - return -1; + goto out_error; /* Someone touched the dir on us. */ goto again; } - page_cache_release(page); } + page_cache_release(page); - cur_off += PAGE_SIZE; + if ((ret = nfs_readdir_offset(inode, goal_cookie)) >= 0) + goto out; + + cur_off += 1; } +out: + return ret; - return 0; +out_error: + if (page) + page_cache_release(page); + goto out; } -static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok) +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int refetch_ok) { struct nfs_readdirargs rd_args; struct nfs_readdirres rd_res; @@ -260,6 +310,7 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs struct inode *inode = dentry->d_inode; struct page *page, **hash; unsigned long page_cache; + long offset; __u32 *cookiep; page = NULL; @@ -267,27 +318,34 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs if (!page_cache) goto out; - while ((cookiep = find_cookie(inode, offset)) == NULL) { + if ((offset = nfs_readdir_offset(inode, cookie)) < 0) { if (!refetch_ok || - refetch_to_readdir_off(file, inode, file->f_pos)) + (offset = refetch_to_readdir_cookie(file, inode)) < 0) { + page_cache_free(page_cache); goto out; + } + } + + cookiep = find_cookie(inode, offset); + if (!cookiep) { + /* Gross fatal error. */ + page_cache_free(page_cache); + goto out; } hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); +repeat: + page = __find_lock_page(inode, offset, hash); if (page) { page_cache_free(page_cache); - goto out; + goto unlock_out; } page = page_cache_entry(page_cache); - atomic_inc(&page->count); - page->flags = ((page->flags & - ~((1 << PG_uptodate) | (1 << PG_error))) | - ((1 << PG_referenced) | (1 << PG_locked))); - page->offset = offset; - add_page_to_inode_queue(inode, page); - __add_page_to_hash_queue(page, hash); + if (add_to_page_cache_unique(page, inode, offset, hash)) { + page_cache_release(page); + goto repeat; + } rd_args.fh = NFS_FH(dentry); rd_res.buffer = (char *)page_cache; @@ -303,48 +361,50 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs } while(rd_res.bufsiz > 0); if (rd_res.bufsiz < 0) - NFS_DIREOF(inode) = - (offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz); + NFS_DIREOF(inode) = rd_res.cookie; else if (create_cookie(rd_res.cookie, offset, inode)) goto error; - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); unlock_out: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out: return page; error: - set_bit(PG_error, &page->flags); + SetPageError(page); goto unlock_out; } -static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff, +/* Seek up to dirent assosciated with the passed in cookie, + * then fill in dirents found. Return the last cookie + * actually given to the user, to update the file position. + */ +static __inline__ u32 nfs_do_filldir(__u32 *p, u32 cookie, void *dirent, filldir_t filldir) { u32 end; - if (doff & ~PAGE_CACHE_MASK) { - doff = find_midpoint(p, doff); - p += (doff & ~PAGE_CACHE_MASK) >> 2; - } while((end = *p++) != 0) { - __u32 fileid = *p++; - __u32 len = *p++; - __u32 skip = NFS_NAMELEN_ALIGN(len); - char *name = (char *) p; - - /* Skip the cookie. */ - p = ((__u32 *) (name + skip)) + 1; - if (filldir(dirent, name, len, doff, fileid) < 0) - goto out; - doff += (skip + (4 * sizeof(__u32))); + __u32 fileid, len, skip, this_cookie; + char *name; + + fileid = *p++; + len = *p++; + name = (char *) p; + skip = NFS_NAMELEN_ALIGN(len); + p += (skip >> 2); + this_cookie = *p++; + + if (this_cookie < cookie) + continue; + + cookie = this_cookie; + if (filldir(dirent, name, len, cookie, fileid) < 0) + break; } - if (!*p) - doff = PAGE_CACHE_ALIGN(doff); -out: - return doff; + + return cookie; } /* The file offset position is represented in pure bytes, to @@ -359,7 +419,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; struct page *page, **hash; - unsigned long offset; + long offset; int res; res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); @@ -369,14 +429,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode)) return 0; - offset = filp->f_pos >> PAGE_CACHE_SHIFT; + if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0) + goto no_dirent_page; + hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); + page = __find_get_page(inode, offset, hash); if (!page) goto no_dirent_page; - if (PageLocked(page)) - goto dirent_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto dirent_read_error; success: filp->f_pos = nfs_do_filldir((__u32 *) page_address(page), @@ -385,13 +445,11 @@ success: return 0; no_dirent_page: - page = try_to_get_dirent_page(filp, offset, 1); + page = try_to_get_dirent_page(filp, filp->f_pos, 1); if (!page) goto no_page; -dirent_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; dirent_read_error: page_cache_release(page); @@ -399,20 +457,39 @@ no_page: return -EIO; } -/* Invalidate directory cookie caches and EOF marker - * for an inode. +/* Flush directory cookie and EOF caches for an inode. + * So we don't thrash allocating/freeing cookie tables, + * we keep the cookies around until the inode is + * deleted/reused. + */ +__inline__ void nfs_flush_dircache(struct inode *inode) +{ + struct nfs_cookie_table *p = NFS_COOKIES(inode); + + while (p != NULL) { + int i; + + for(i = 0; i < COOKIES_PER_CHUNK; i++) + p->cookies[i] = 0; + + p = p->next; + } + NFS_DIREOF(inode) = 0; +} + +/* Free up directory cache state, this happens when + * nfs_delete_inode is called on an NFS directory. */ -__inline__ void nfs_invalidate_dircache(struct inode *inode) +void nfs_free_dircache(struct inode *inode) { struct nfs_cookie_table *p = NFS_COOKIES(inode); - if (p != NULL) { - NFS_COOKIES(inode) = NULL; - do { struct nfs_cookie_table *next = p->next; - kmem_cache_free(nfs_cookie_cachep, p); - p = next; - } while (p != NULL); + while (p != NULL) { + struct nfs_cookie_table *next = p->next; + kmem_cache_free(nfs_cookie_cachep, p); + p = next; } + NFS_COOKIES(inode) = NULL; NFS_DIREOF(inode) = 0; } @@ -538,11 +615,11 @@ out_bad: /* Purge readdir caches. */ if (dentry->d_parent->d_inode) { invalidate_inode_pages(dentry->d_parent->d_inode); - nfs_invalidate_dircache(dentry->d_parent->d_inode); + nfs_flush_dircache(dentry->d_parent->d_inode); } if (inode && S_ISDIR(inode->i_mode)) { invalidate_inode_pages(inode); - nfs_invalidate_dircache(inode); + nfs_flush_dircache(inode); } return 0; } @@ -739,7 +816,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) * Invalidate the dir cache before the operation to avoid a race. */ invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); if (!error) @@ -769,7 +846,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); if (!error) @@ -804,7 +881,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); return error; @@ -825,7 +902,7 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink); #endif invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); @@ -953,7 +1030,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); } while(sdentry->d_inode != NULL); /* need negative lookup */ invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, NFS_FH(dentry->d_parent), silly); @@ -1023,7 +1100,7 @@ inode->i_count, inode->i_nlink); d_delete(dentry); } invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); /* @@ -1090,7 +1167,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, symname, &sattr); if (!error) { @@ -1121,7 +1198,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry), NFS_FH(dentry->d_parent), dentry->d_name.name); if (!error) { @@ -1267,9 +1344,9 @@ new_inode->i_count, new_inode->i_nlink); } invalidate_inode_pages(new_dir); - nfs_invalidate_dircache(new_dir); + nfs_flush_dircache(new_dir); invalidate_inode_pages(old_dir); - nfs_invalidate_dircache(old_dir); + nfs_flush_dircache(old_dir); error = nfs_proc_rename(NFS_DSERVER(old_dentry), NFS_FH(old_dentry->d_parent), old_dentry->d_name.name, NFS_FH(new_dentry->d_parent), new_dentry->d_name.name); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 75b149886..d3066f4cd 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -26,6 +26,7 @@ #include <linux/malloc.h> #include <linux/pagemap.h> #include <linux/lockd/bind.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/segment.h> @@ -70,13 +71,13 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ nfs_revalidate, /* revalidate */ }; @@ -172,8 +173,11 @@ static long nfs_write_one_page(struct file *file, struct page *page, unsigned lo bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); status = -EFAULT; - if (bytes) + if (bytes) { + lock_kernel(); status = nfs_updatepage(file, page, offset, bytes); + unlock_kernel(); + } return status; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c7e684763..5421cebf9 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -99,23 +99,28 @@ nfs_delete_inode(struct inode * inode) int failed; dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); - /* - * Flush out any pending write requests ... - */ - if (NFS_WRITEBACK(inode) != NULL) { - unsigned long timeout = jiffies + 5*HZ; + + if (S_ISDIR(inode->i_mode)) { + nfs_free_dircache(inode); + } else { + /* + * Flush out any pending write requests ... + */ + if (NFS_WRITEBACK(inode) != NULL) { + unsigned long timeout = jiffies + 5*HZ; #ifdef NFS_DEBUG_VERBOSE printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); #endif - nfs_inval(inode); - while (NFS_WRITEBACK(inode) != NULL && - time_before(jiffies, timeout)) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + nfs_inval(inode); + while (NFS_WRITEBACK(inode) != NULL && + time_before(jiffies, timeout)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + } + current->state = TASK_RUNNING; + if (NFS_WRITEBACK(inode) != NULL) + printk("NFS: Arghhh, stuck RPC requests!\n"); } - current->state = TASK_RUNNING; - if (NFS_WRITEBACK(inode) != NULL) - printk("NFS: Arghhh, stuck RPC requests!\n"); } failed = nfs_check_failed_request(inode); @@ -433,7 +438,7 @@ nfs_zap_caches(struct inode *inode) invalidate_inode_pages(inode); if (S_ISDIR(inode->i_mode)) - nfs_invalidate_dircache(inode); + nfs_flush_dircache(inode); } /* @@ -477,8 +482,6 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_size = fattr->size; inode->i_mtime = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds; - NFS_COOKIES(inode) = NULL; - NFS_WRITEBACK(inode) = NULL; } nfs_refresh_inode(inode, fattr); } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index f606b76e4..843f6b23e 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -26,6 +26,7 @@ #include <linux/pagemap.h> #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> +#include <linux/smp_lock.h> #include <asm/segment.h> #include <asm/system.h> @@ -77,7 +78,6 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0; dprintk("NFS: nfs_readpage_sync(%p)\n", page); - clear_bit(PG_error, &page->flags); do { if (count < rsize) @@ -111,16 +111,14 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) } while (count); memset(buffer, 0, count); - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); result = 0; io_error: + UnlockPage(page); /* Note: we don't refresh if the call returned error */ if (refresh && result >= 0) nfs_refresh_inode(inode, &rqst.ra_fattr); - /* N.B. Use nfs_unlock_page here? */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); return result; } @@ -146,17 +144,15 @@ nfs_readpage_result(struct rpc_task *task) memset((char *) address + result, 0, PAGE_SIZE - result); } nfs_refresh_inode(req->ra_inode, &req->ra_fattr); - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); succ++; } else { - set_bit(PG_error, &page->flags); + SetPageError(page); fail++; dprintk("NFS: %d successful reads, %d failures\n", succ, fail); } - /* N.B. Use nfs_unlock_page here? */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - + page->owner = (int)current; // HACK, FIXME, will go away. + UnlockPage(page); free_page(address); rpc_release_task(task); @@ -227,10 +223,10 @@ nfs_readpage(struct file *file, struct page *page) struct inode *inode = dentry->d_inode; int error; + lock_kernel(); dprintk("NFS: nfs_readpage (%p %ld@%ld)\n", page, PAGE_SIZE, page->offset); - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); + get_page(page); /* * Try to flush any pending writes to the file.. @@ -256,10 +252,10 @@ nfs_readpage(struct file *file, struct page *page) goto out_free; out_error: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out_free: free_page(page_address(page)); out: + unlock_kernel(); return error; } diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index c93dce2fe..c6fc4d685 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -43,11 +43,14 @@ struct inode_operations nfs_symlink_inode_operations = { NULL, /* rename */ nfs_readlink, /* readlink */ nfs_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* Symlink caching in the page cache is even more simplistic @@ -65,20 +68,18 @@ static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode goto out; hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); +repeat: + page = __find_lock_page(inode, 0, hash); if (page) { page_cache_free(page_cache); - goto out; + goto unlock_out; } page = page_cache_entry(page_cache); - atomic_inc(&page->count); - page->flags = ((page->flags & - ~((1 << PG_uptodate) | (1 << PG_error))) | - ((1 << PG_referenced) | (1 << PG_locked))); - page->offset = 0; - add_page_to_inode_queue(inode, page); - __add_page_to_hash_queue(page, hash); + if (add_to_page_cache_unique(page, inode, 0, hash)) { + page_cache_release(page); + goto repeat; + } /* We place the length at the beginning of the page, * in host byte order, followed by the string. The @@ -89,32 +90,28 @@ static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &rl_args, NULL, 0) < 0) goto error; - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); unlock_out: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out: return page; error: - set_bit(PG_error, &page->flags); + SetPageError(page); goto unlock_out; } static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) { struct inode *inode = dentry->d_inode; - struct page *page, **hash; + struct page *page; u32 *p, len; /* Caller revalidated the directory inode already. */ - hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); + page = find_get_page(inode, 0); if (!page) goto no_readlink_page; - if (PageLocked(page)) - goto readlink_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto readlink_read_error; success: p = (u32 *) page_address(page); @@ -129,9 +126,7 @@ no_readlink_page: page = try_to_get_symlink_page(dentry, inode); if (!page) goto no_page; -readlink_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; readlink_read_error: page_cache_release(page); @@ -144,17 +139,14 @@ nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) { struct dentry *result; struct inode *inode = dentry->d_inode; - struct page *page, **hash; + struct page *page; u32 *p; /* Caller revalidated the directory inode already. */ - hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); + page = find_get_page(inode, 0); if (!page) goto no_followlink_page; - if (PageLocked(page)) - goto followlink_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto followlink_read_error; success: p = (u32 *) page_address(page); @@ -166,9 +158,7 @@ no_followlink_page: page = try_to_get_symlink_page(dentry, inode); if (!page) goto no_page; -followlink_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; followlink_read_error: page_cache_release(page); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 8da08f06b..911a5261e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -55,6 +55,7 @@ #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> #include <asm/uaccess.h> +#include <linux/smp_lock.h> #define NFS_PARANOIA 1 #define NFSDBG_FACILITY NFSDBG_PAGECACHE @@ -93,6 +94,7 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode, u8 *buffer; struct nfs_fattr fattr; + lock_kernel(); dprintk("NFS: nfs_writepage_sync(%s/%s %d@%ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, page->offset + offset); @@ -110,7 +112,7 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode, if (result < 0) { /* Must mark the page invalid after I/O error */ - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); goto io_error; } if (result != wsize) @@ -153,6 +155,7 @@ io_error: inode->i_ino, fattr.fileid); } + unlock_kernel(); return written? written : result; } @@ -463,7 +466,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * Ok, there's another user of this page with the new request.. * The IO completion will then free the page and the dentry. */ - atomic_inc(&page->count); + get_page(page); file->f_count++; /* Schedule request */ @@ -471,7 +474,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig updated: if (req->wb_bytes == PAGE_SIZE) - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); retval = count; if (synchronous) { @@ -486,7 +489,7 @@ updated: } if (retval < 0) - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); } free_write_request(req); @@ -682,7 +685,7 @@ nfs_wback_result(struct rpc_task *task) rpc_release_task(task); if (WB_INVALIDATE(req)) - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); __free_page(page); remove_write_request(&NFS_WRITEBACK(inode), req); diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index a43e071fe..d9430c2cc 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -445,7 +445,6 @@ static struct inode_operations ntfs_inode_operations_nobmap = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -628,7 +627,6 @@ static struct inode_operations ntfs_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -677,7 +675,6 @@ static struct inode_operations ntfs_dir_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -7,6 +7,7 @@ #include <linux/mm.h> #include <linux/file.h> #include <linux/poll.h> +#include <linux/malloc.h> #include <asm/uaccess.h> @@ -101,9 +102,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf, free = count; else free = 1; /* can't do it atomically, wait for any free space */ - up(&inode->i_sem); - if (down_interruptible(&inode->i_atomic_write)) { - down(&inode->i_sem); + if (down_interruptible(&inode->i_sem)) { return -ERESTARTSYS; } while (count>0) { @@ -144,8 +143,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf, inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); errout: - up(&inode->i_atomic_write); - down(&inode->i_sem); + up(&inode->i_sem); return written ? written : err; } @@ -249,8 +247,11 @@ static unsigned int connect_poll(struct file * filp, poll_table * wait) static int pipe_release(struct inode * inode) { if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { - free_page((unsigned long) PIPE_BASE(*inode)); - PIPE_BASE(*inode) = NULL; + struct pipe_inode_info *info = inode->i_pipe; + inode->i_pipe = NULL; + free_page((unsigned long) info->base); + kfree(info); + return 0; } wake_up_interruptible(&PIPE_WAIT(*inode)); return 0; @@ -404,36 +405,48 @@ static struct inode * get_pipe_inode(void) { extern struct inode_operations pipe_inode_operations; struct inode *inode = get_empty_inode(); + unsigned long page; - if (inode) { - unsigned long page = __get_free_page(GFP_USER); - - if (!page) { - iput(inode); - inode = NULL; - } else { - PIPE_BASE(*inode) = (char *) page; - inode->i_op = &pipe_inode_operations; - init_waitqueue_head(&PIPE_WAIT(*inode)); - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - PIPE_LOCK(*inode) = 0; - /* - * Mark the inode dirty from the very beginning, - * that way it will never be moved to the dirty - * list because "mark_inode_dirty()" will think - * that it already _is_ on the dirty list. - */ - inode->i_state = I_DIRTY; - inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_blksize = PAGE_SIZE; - } - } + if (!inode) + goto fail_inode; + + page = __get_free_page(GFP_USER); + + if (!page) + goto fail_iput; + + /* XXX */ + inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); + if (!inode->i_pipe) + goto fail_page; + + PIPE_BASE(*inode) = (char *) page; + inode->i_op = &pipe_inode_operations; + init_waitqueue_head(&PIPE_WAIT(*inode)); + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; + PIPE_LOCK(*inode) = 0; + /* + * Mark the inode dirty from the very beginning, + * that way it will never be moved to the dirty + * list because "mark_inode_dirty()" will think + * that it already _is_ on the dirty list. + */ + inode->i_state = I_DIRTY; + inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = PAGE_SIZE; return inode; + +fail_page: + free_page(page); +fail_iput: + iput(inode); +fail_inode: + return NULL; } struct inode_operations pipe_inode_operations = { @@ -448,11 +461,14 @@ struct inode_operations pipe_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; int do_pipe(int *fd) @@ -513,6 +529,8 @@ close_f12_inode_i: put_unused_fd(i); close_f12_inode: free_page((unsigned long) PIPE_BASE(*inode)); + kfree(inode->i_pipe); + inode->i_pipe = NULL; iput(inode); close_f12: put_filp(f2); diff --git a/fs/proc/array.c b/fs/proc/array.c index 1bc76ff2f..5e88d0566 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -348,7 +348,7 @@ static int get_meminfo(char * buffer) len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" "Swap: %8lu %8lu %8lu\n", - i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE, + i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, atomic_read(&page_cache_size)*PAGE_SIZE, i.totalswap, i.totalswap-i.freeswap, i.freeswap); /* * Tagged format, for easy grepping and expansion. The above will go away @@ -359,14 +359,14 @@ static int get_meminfo(char * buffer) "MemFree: %8lu kB\n" "MemShared: %8lu kB\n" "Buffers: %8lu kB\n" - "Cached: %8lu kB\n" + "Cached: %8u kB\n" "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n", i.totalram >> 10, i.freeram >> 10, i.sharedram >> 10, i.bufferram >> 10, - page_cache_size << (PAGE_SHIFT - 10), + atomic_read(&page_cache_size) << (PAGE_SHIFT - 10), i.totalswap >> 10, i.freeswap >> 10); } @@ -997,7 +997,7 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned ++*dirty; if (MAP_NR(pte_page(page)) >= max_mapnr) continue; - if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) > 1) + if (page_count(mem_map + MAP_NR(pte_page(page))) > 1) ++*shared; } while (address < end); } @@ -1348,6 +1348,9 @@ static long get_root_array(char * page, int type, char **start, case PROC_IOPORTS: return get_ioport_list(page); + + case PROC_MEMORY: + return get_mem_list(page); #ifdef CONFIG_BLK_DEV_MD case PROC_MD: return get_md_status(page); @@ -1542,11 +1545,14 @@ struct inode_operations proc_array_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static ssize_t arraylong_read(struct file * file, char * buf, @@ -1590,9 +1596,12 @@ struct inode_operations proc_arraylong_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/base.c b/fs/proc/base.c index c9b2d8649..8579dd8c5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -45,11 +45,14 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 1defdbae1..2bbb51d28 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -51,11 +51,14 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 54b16f84b..4e59fed73 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -51,20 +51,23 @@ struct inode_operations proc_file_inode_operations = { &proc_file_operations, /* default proc file-ops */ NULL, /* create */ NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -83,11 +86,14 @@ struct inode_operations proc_net_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index ba78768b6..3cfccab96 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -72,9 +72,12 @@ struct inode_operations proc_kmsg_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/link.c b/fs/proc/link.c index 9df4de674..3a5639825 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -49,11 +49,14 @@ struct inode_operations proc_link_inode_operations = { NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * proc_follow_link(struct dentry *dentry, diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 117587d8d..0e89f7645 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -298,7 +298,7 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) set_pte(dest_table, *src_table); mapnr = MAP_NR(pte_page(*src_table)); if (mapnr < max_mapnr) - atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count); + get_page(mem_map + MAP_NR(pte_page(*src_table))); stmp += PAGE_SIZE; dtmp += PAGE_SIZE; @@ -336,9 +336,12 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/net.c b/fs/proc/net.c index a6d8c5616..1ad226de0 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -113,9 +113,12 @@ struct inode_operations proc_net_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index dbf2b32b9..562aa11c5 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -277,22 +277,24 @@ static struct file_operations omirr_operations = { }; struct inode_operations proc_omirr_inode_operations = { - &omirr_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* smap */ + &omirr_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index cd4aca324..594f00858 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -57,12 +57,14 @@ struct inode_operations devtree_symlink_inode_operations = { NULL, /* rename */ devtree_readlink, /* readlink */ devtree_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry *devtree_follow_link(struct dentry *dentry, diff --git a/fs/proc/root.c b/fs/proc/root.c index f6a775359..67171fa07 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -71,11 +71,14 @@ struct inode_operations proc_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -94,11 +97,14 @@ struct inode_operations proc_dyna_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -136,11 +142,14 @@ static struct inode_operations proc_root_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -293,11 +302,14 @@ struct inode_operations proc_openprom_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; struct proc_dir_entry proc_openprom = { @@ -478,11 +490,14 @@ static struct inode_operations proc_self_inode_operations = { NULL, /* rename */ proc_self_readlink, /* readlink */ proc_self_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct inode_operations proc_link_inode_operations = { @@ -498,11 +513,14 @@ static struct inode_operations proc_link_inode_operations = { NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct proc_dir_entry proc_root_loadavg = { @@ -621,6 +639,11 @@ static struct proc_dir_entry proc_root_ioports = { S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; +static struct proc_dir_entry proc_root_memory = { + PROC_MEMORY, 6, "memory", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations +}; static struct proc_dir_entry proc_root_cmdline = { PROC_CMDLINE, 7, "cmdline", S_IFREG | S_IRUGO, 1, 0, 0, @@ -716,6 +739,7 @@ __initfunc(void proc_root_init(void)) proc_register(&proc_root, &proc_root_fs); proc_register(&proc_root, &proc_root_dma); proc_register(&proc_root, &proc_root_ioports); + proc_register(&proc_root, &proc_root_memory); proc_register(&proc_root, &proc_root_cmdline); #ifdef CONFIG_RTC proc_register(&proc_root, &proc_root_rtc); diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index 6f3ad0770..ae2679b6d 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -59,23 +59,26 @@ static struct file_operations proc_scsi_operations = { * proc directories can do almost nothing.. */ struct inode_operations proc_scsi_inode_operations = { - &proc_scsi_operations, /* default scsi directory file-ops */ - NULL, /* create */ - proc_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ +&proc_scsi_operations, /* default scsi directory file-ops */ + NULL, /* create */ + proc_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; int get_not_present_info(char *buffer, char **start, off_t offset, int length) diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c index eab3e3186..c6e32894d 100644 --- a/fs/proc/sysvipc.c +++ b/fs/proc/sysvipc.c @@ -118,21 +118,24 @@ static struct file_operations proc_sysvipc_operations = { * proc directories can do almost nothing.. */ struct inode_operations proc_sysvipc_inode_operations = { - &proc_sysvipc_operations, /* default net file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ + &proc_sysvipc_operations, /* default net file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/read_write.c b/fs/read_write.c index 7b9bf0bf7..c7ea90a69 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -166,9 +166,7 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) if (!file->f_op || !(write = file->f_op->write)) goto out; - down(&inode->i_sem); ret = write(file, buf, count, &file->f_pos); - up(&inode->i_sem); out: fput(file); bad_file: @@ -304,9 +302,7 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, if (!file) goto bad_file; if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) { - down(&file->f_dentry->d_inode->i_sem); ret = do_readv_writev(VERIFY_READ, file, vector, count); - up(&file->f_dentry->d_inode->i_sem); } fput(file); @@ -376,10 +372,7 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, if (pos < 0) goto out; - down(&file->f_dentry->d_inode->i_sem); ret = write(file, buf, count, &pos); - up(&file->f_dentry->d_inode->i_sem); - out: fput(file); bad_file: diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index b820642fe..870ebac74 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -65,7 +65,6 @@ struct inode_operations smb_dir_inode_operations = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ smb_revalidate_inode, /* revalidate */ }; diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 2611ceb61..cfb2d82da 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -14,6 +14,7 @@ #include <linux/mm.h> #include <linux/malloc.h> #include <linux/pagemap.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -271,8 +272,11 @@ static long smb_write_one_page(struct file *file, struct page *page, unsigned lo bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); status = -EFAULT; - if (bytes) + if (bytes) { + lock_kernel(); status = smb_updatepage(file, page, offset, bytes); + unlock_kernel(); + } return status; } @@ -406,6 +410,5 @@ struct inode_operations smb_file_inode_operations = NULL, /* truncate */ smb_file_permission, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ smb_revalidate_inode, /* revalidate */ }; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index d43292af5..627186cae 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -376,9 +376,6 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) *mnt = *((struct smb_mount_data *) raw_data); /* ** temp ** pass config flags in file mode */ mnt->version = (mnt->file_mode >> 9); -#ifdef CONFIG_SMB_WIN95 - mnt->version |= SMB_FIX_WIN95; -#endif mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); mnt->file_mode |= S_IFREG; mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); @@ -387,8 +384,6 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) /* * Display the enabled options */ - if (mnt->version & SMB_FIX_WIN95) - printk("SMBFS: Win 95 bug fixes enabled\n"); if (mnt->version & SMB_FIX_OLDATTR) printk("SMBFS: Using core getattr (Win 95 speedup)\n"); else if (mnt->version & SMB_FIX_DIRATTR) diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 42981d483..06cd5dda9 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -39,6 +39,9 @@ #define SMB_DIRINFO_SIZE 43 #define SMB_STATUS_SIZE 21 +/* yes, this deliberately has two parts */ +#define DENTRY_PATH(dentry) (dentry)->d_parent->d_name.name,(dentry)->d_name.name + static int smb_proc_setattr_ext(struct smb_sb_info *, struct inode *, struct smb_fattr *); static inline int @@ -174,24 +177,22 @@ static int day_n[] = /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ -extern struct timezone sys_tz; - static time_t -utc2local(time_t time) +utc2local(struct smb_sb_info *server, time_t time) { - return time - sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 :0); + return time - server->opt.serverzone*60; } static time_t -local2utc(time_t time) +local2utc(struct smb_sb_info *server, time_t time) { - return time + sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0); + return time + server->opt.serverzone*60; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ static time_t -date_dos2unix(__u16 date, __u16 time) +date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) { int month, year; time_t secs; @@ -202,18 +203,19 @@ date_dos2unix(__u16 date, __u16 time) ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); /* days since 1.1.70 plus 80's leap day */ - return local2utc(secs); + return local2utc(server, secs); } /* Convert linear UNIX date to a MS-DOS time/date pair. */ static void -date_unix2dos(int unix_date, __u16 *date, __u16 *time) +date_unix2dos(struct smb_sb_info *server, + int unix_date, __u16 *date, __u16 *time) { int day, year, nl_day, month; - unix_date = utc2local(unix_date); + unix_date = utc2local(server, unix_date); *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + (((unix_date / 3600) % 24) << 11); @@ -355,6 +357,11 @@ smb_errno(struct smb_sb_info *server) int error = server->err; char *class = "Unknown"; +#ifdef SMBFS_DEBUG_VERBOSE + printk("smb_errno: errcls %d code %d from command 0x%x\n", + errcls, error, SMB_CMD(server->packet)); +#endif + if (errcls == ERRDOS) switch (error) { @@ -456,7 +463,7 @@ smb_errno(struct smb_sb_info *server) class = "ERRCMD"; err_unknown: - printk("smb_errno: class %s, code %d from command %x\n", + printk("smb_errno: class %s, code %d from command 0x%x\n", class, error, SMB_CMD(server->packet)); return EIO; } @@ -646,9 +653,27 @@ printk("smb_newconn: fd=%d, pid=%d\n", opt->fd, current->pid); server->generation += 1; server->state = CONN_VALID; error = 0; + + /* check if we have an old smbmount that uses seconds for the + serverzone */ + if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) + server->opt.serverzone /= 60; + + /* now that we have an established connection we can detect the server + type and enable bug workarounds */ + if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + server->mnt->version |= SMB_FIX_WIN95; +#ifdef SMBFS_DEBUG_VERBOSE + printk("smb_newconn: detected WIN95 server\n"); +#endif + } + #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d\n", -server->opt.protocol, server->opt.max_xmit, server->conn_pid); +printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", + server->opt.protocol, server->opt.max_xmit, server->conn_pid, + server->opt.capabilities); #endif out: @@ -755,7 +780,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_open: %s/%s R/W failed, error=%d, retrying R/O\n", -dentry->d_parent->d_name.name, dentry->d_name.name, error); + DENTRY_PATH(dentry), error); #endif mode = read_only; goto retry; @@ -789,7 +814,7 @@ smb_open(struct dentry *dentry, int wish) if (!inode) { printk("smb_open: no inode for dentry %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); + DENTRY_PATH(dentry)); goto out; } @@ -810,7 +835,7 @@ smb_open(struct dentry *dentry, int wish) { #ifdef SMBFS_PARANOIA printk("smb_open: %s/%s open failed, result=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, result); + DENTRY_PATH(dentry), result); #endif goto out; } @@ -829,8 +854,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result); { #ifdef SMBFS_PARANOIA printk("smb_open: %s/%s access denied, access=%x, wish=%x\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->u.smbfs_i.access, wish); + DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish); #endif result = -EACCES; } @@ -845,7 +869,7 @@ smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) { smb_setup_header(server, SMBclose, 3, 0); WSET(server->packet, smb_vwv0, fileid); - DSET(server->packet, smb_vwv1, utc2local(mtime)); + DSET(server->packet, smb_vwv1, utc2local(server, mtime)); return smb_request_ok(server, SMBclose, 0, 0); } @@ -946,7 +970,7 @@ smb_close_dentry(struct dentry * dentry) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_close_dentry: closing %s/%s, count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); + DENTRY_PATH(dentry), dentry->d_count); #endif smb_proc_close_inode(server, ino); } @@ -954,7 +978,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); } #ifdef SMBFS_DEBUG_VERBOSE printk("smb_close_dentry: closed %s/%s, count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); + DENTRY_PATH(dentry), dentry->d_count); #endif } } @@ -1014,7 +1038,7 @@ smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data) out: #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_read: file %s/%s, count=%d, result=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, count, result); + DENTRY_PATH(dentry), count, result); #endif smb_unlock_server(server); return result; @@ -1029,8 +1053,7 @@ smb_proc_write(struct dentry *dentry, off_t offset, int count, const char *data) #if SMBFS_DEBUG_VERBOSE printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -count, offset, server->packet_size); + DENTRY_PATH(dentry), count, offset, server->packet_size); #endif smb_lock_server(server); p = smb_setup_header(server, SMBwrite, 5, count + 3); @@ -1063,7 +1086,7 @@ smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) retry: p = smb_setup_header(server, SMBcreate, 3, 0); WSET(server->packet, smb_vwv0, attr); - DSET(server->packet, smb_vwv1, utc2local(ctime)); + DSET(server->packet, smb_vwv1, utc2local(server, ctime)); *p++ = 4; p = smb_encode_path(server, p, dentry, NULL); smb_setup_bcc(server, p); @@ -1323,7 +1346,7 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos, #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_readdir_short: %s/%s, pos=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, fpos); + DENTRY_PATH(dir), fpos); #endif smb_lock_server(server); @@ -1804,15 +1827,15 @@ mask, resp_data_len, WVAL(resp_param, 2)); */ date = WVAL(resp_data, 0); time = WVAL(resp_data, 2); - fattr->f_ctime = date_dos2unix(date, time); + fattr->f_ctime = date_dos2unix(server, date, time); date = WVAL(resp_data, 4); time = WVAL(resp_data, 6); - fattr->f_atime = date_dos2unix(date, time); + fattr->f_atime = date_dos2unix(server, date, time); date = WVAL(resp_data, 8); time = WVAL(resp_data, 10); - fattr->f_mtime = date_dos2unix(date, time); + fattr->f_mtime = date_dos2unix(server, date, time); #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n", mask, date, time, fattr->f_mtime); @@ -1831,7 +1854,7 @@ out: */ static int smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) + struct smb_fattr *fattr) { int result; char *p; @@ -1849,13 +1872,13 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, goto out; } fattr->attr = WVAL(server->packet, smb_vwv0); - fattr->f_mtime = local2utc(DVAL(server->packet, smb_vwv1)); + fattr->f_mtime = local2utc(server, DVAL(server->packet, smb_vwv1)); fattr->f_size = DVAL(server->packet, smb_vwv3); fattr->f_ctime = fattr->f_mtime; fattr->f_atime = fattr->f_mtime; #ifdef SMBFS_DEBUG_TIMESTAMP printk("getattr_core: %s/%s, mtime=%ld\n", -dir->d_name.name, name->name, fattr->f_mtime); + DENTRY_PATH(dir), fattr->f_mtime); #endif result = 0; @@ -1926,18 +1949,18 @@ printk("smb_proc_getattr_trans2: not enough data for %s, len=%d\n", } date = WVAL(resp_data, off_date); time = WVAL(resp_data, off_time); - attr->f_ctime = date_dos2unix(date, time); + attr->f_ctime = date_dos2unix(server, date, time); date = WVAL(resp_data, 4 + off_date); time = WVAL(resp_data, 4 + off_time); - attr->f_atime = date_dos2unix(date, time); + attr->f_atime = date_dos2unix(server, date, time); date = WVAL(resp_data, 8 + off_date); time = WVAL(resp_data, 8 + off_time); - attr->f_mtime = date_dos2unix(date, time); + attr->f_mtime = date_dos2unix(server, date, time); #ifdef SMBFS_DEBUG_TIMESTAMP printk("getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", -dir->d_name.name, name->name, date, time, attr->f_mtime); + DENTRY_PATH(dir), date, time, attr->f_mtime); #endif attr->f_size = DVAL(resp_data, 12); attr->attr = WVAL(resp_data, 20); @@ -1980,6 +2003,7 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) return result; } + /* * Called with the server locked. Because of bugs in the * core protocol, we use this only to set attributes. See @@ -1994,7 +2018,7 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) */ static int smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr) + __u16 attr) { char *p; int result; @@ -2009,11 +2033,6 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, WSET(server->packet, smb_vwv6, 0); WSET(server->packet, smb_vwv7, 0); *p++ = 4; - /* - * Samba uses three leading '\', so we'll do it too. - */ - *p++ = '\\'; - *p++ = '\\'; p = smb_encode_path(server, p, dentry, NULL); *p++ = 4; *p++ = 0; @@ -2044,7 +2063,7 @@ smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr) #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_setattr: setting %s/%s, open=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode)); + DENTRY_PATH(dir), smb_is_open(dir->d_inode)); #endif smb_lock_server(server); result = smb_proc_setattr_core(server, dir, fattr->attr); @@ -2069,10 +2088,10 @@ smb_proc_setattr_ext(struct smb_sb_info *server, /* We don't change the creation time */ WSET(server->packet, smb_vwv1, 0); WSET(server->packet, smb_vwv2, 0); - date_unix2dos(fattr->f_atime, &date, &time); + date_unix2dos(server, fattr->f_atime, &date, &time); WSET(server->packet, smb_vwv3, date); WSET(server->packet, smb_vwv4, time); - date_unix2dos(fattr->f_mtime, &date, &time); + date_unix2dos(server, fattr->f_mtime, &date, &time); WSET(server->packet, smb_vwv5, date); WSET(server->packet, smb_vwv6, time); #ifdef SMBFS_DEBUG_TIMESTAMP @@ -2119,15 +2138,15 @@ smb_proc_setattr_trans2(struct smb_sb_info *server, WSET(data, 0, 0); /* creation time */ WSET(data, 2, 0); - date_unix2dos(fattr->f_atime, &date, &time); + date_unix2dos(server, fattr->f_atime, &date, &time); WSET(data, 4, date); WSET(data, 6, time); - date_unix2dos(fattr->f_mtime, &date, &time); + date_unix2dos(server, fattr->f_mtime, &date, &time); WSET(data, 8, date); WSET(data, 10, time); #ifdef SMBFS_DEBUG_TIMESTAMP printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", -dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime); + DENTRY_PATH(dir), date, time, fattr->f_mtime); #endif DSET(data, 12, 0); /* size */ DSET(data, 16, 0); /* blksize */ @@ -2174,10 +2193,12 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_settime: setting %s/%s, open=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode)); + DENTRY_PATH(dentry), smb_is_open(inode)); #endif smb_lock_server(server); - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + /* setting the time on a Win95 server fails (tridge) */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && + !(server->mnt->version & SMB_FIX_WIN95)) { if (smb_is_open(inode) && inode->u.smbfs_i.access != SMB_O_RDONLY) @@ -2194,12 +2215,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode)); { /* * Set the mtime by opening and closing the file. + * Note that the file is opened read-only, but this + * still allows us to set the date (tridge) */ result = -EACCES; if (!smb_is_open(inode)) - smb_proc_open(server, dentry, SMB_O_WRONLY); - if (smb_is_open(inode) && - inode->u.smbfs_i.access != SMB_O_RDONLY) + smb_proc_open(server, dentry, SMB_O_RDONLY); + if (smb_is_open(inode)) { inode->i_mtime = fattr->f_mtime; result = smb_proc_close_inode(server, inode); diff --git a/fs/super.c b/fs/super.c index 9996d444b..5cf518959 100644 --- a/fs/super.c +++ b/fs/super.c @@ -918,13 +918,6 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) int retval; struct vfsmount *vfsmnt; - /* - * Invalidate the inodes, as some mount options may be changed. - * N.B. If we are changing media, we should check the return - * from invalidate_inodes ... can't allow _any_ open files. - */ - invalidate_inodes(sb); - if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev)) return -EACCES; /*flags |= MS_RDONLY;*/ @@ -941,6 +934,14 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) vfsmnt = lookup_vfsmnt(sb->s_dev); if (vfsmnt) vfsmnt->mnt_flags = sb->s_flags; + + /* + * Invalidate the inodes, as some mount options may be changed. + * N.B. If we are changing media, we should check the return + * from invalidate_inodes ... can't allow _any_ open files. + */ + invalidate_inodes(sb); + return 0; } diff --git a/fs/sysv/file.c b/fs/sysv/file.c index d60be8fa5..19443f289 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -33,7 +33,51 @@ #include <linux/fs.h> #include <linux/sysv_fs.h> -static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *); +static int sysv_writepage (struct file * file, struct page * page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/512]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE >> inode->i_sb->sv_block_size_bits; + block = page->offset >> inode->i_sb->sv_block_size_bits; + p = nr; + bh = page->buffers; + do { + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; + else + *p = sysv_getblk_block (inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; + block++; + p++; + if (bh) + bh = bh->b_this_page; + } while (i > 0); + + /* IO start */ + brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->sv_block_size, 1); + return 0; +} + +static long sysv_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, sysv_getblk_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, sysv_write_one_page); +} /* * We have mostly NULLs here: the current defaults are OK for @@ -41,7 +85,7 @@ static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *); */ static struct file_operations sysv_file_operations = { NULL, /* lseek - default */ - sysv_file_read, /* read */ + generic_file_read, /* read */ sysv_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* poll - default */ @@ -50,7 +94,10 @@ static struct file_operations sysv_file_operations = { NULL, /* no special open is needed */ NULL, /* flush */ NULL, /* release */ - sysv_sync_file /* fsync */ + sysv_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ }; struct inode_operations sysv_file_inode_operations = { @@ -67,208 +114,11 @@ struct inode_operations sysv_file_inode_operations = { NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ - NULL, /* writepage */ + sysv_writepage, /* writepage */ sysv_bmap, /* bmap */ sysv_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ }; - -ssize_t sysv_file_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - ssize_t read,left,chars; - size_t block; - ssize_t blocks, offset; - int bhrequest, uptodate; - struct buffer_head ** bhb, ** bhe; - struct buffer_head * bhreq[NBUF]; - struct buffer_head * buflist[NBUF]; - size_t size; - - if (!inode) { - printk("sysv_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - offset = *ppos; - size = inode->i_size; - if (offset > size) - left = 0; - else - left = size - offset; - if (left > count) - left = count; - if (left <= 0) - return 0; - read = 0; - block = offset >> sb->sv_block_size_bits; - offset &= sb->sv_block_size_1; - size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits; - blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits; - bhb = bhe = buflist; - if (filp->f_reada) { - blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9); - if (block + blocks > size) - blocks = size - block; - } - - /* We do this in a two stage process. We first try to request - as many blocks as we can, then we wait for the first one to - complete, and then we try to wrap up as many as are actually - done. This routine is rather generic, in that it can be used - in a filesystem by substituting the appropriate function in - for getblk. - - This routine is optimized to make maximum use of the various - buffers and caches. - */ - - do { - bhrequest = 0; - uptodate = 1; - while (blocks) { - --blocks; - *bhb = sysv_getblk(inode, block++, 0); - if (*bhb && !buffer_uptodate(*bhb)) { - uptodate = 0; - bhreq[bhrequest++] = *bhb; - } - - if (++bhb == &buflist[NBUF]) - bhb = buflist; - - /* If the block we have on hand is uptodate, go ahead - and complete processing. */ - if (uptodate) - break; - if (bhb == bhe) - break; - } - - /* Now request them all */ - if (bhrequest) - ll_rw_block(READ, bhrequest, bhreq); - - do { /* Finish off all I/O that has actually completed */ - if (*bhe) { - wait_on_buffer(*bhe); - if (!buffer_uptodate(*bhe)) { /* read error? */ - brelse(*bhe); - if (++bhe == &buflist[NBUF]) - bhe = buflist; - left = 0; - break; - } - } - if (left < sb->sv_block_size - offset) - chars = left; - else - chars = sb->sv_block_size - offset; - *ppos += chars; - left -= chars; - read += chars; - if (*bhe) { - copy_to_user(buf,offset+(*bhe)->b_data,chars); - brelse(*bhe); - buf += chars; - } else { - while (chars-- > 0) - put_user(0,buf++); - } - offset = 0; - if (++bhe == &buflist[NBUF]) - bhe = buflist; - } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); - } while (left > 0); - -/* Release the read-ahead blocks */ - while (bhe != bhb) { - brelse(*bhe); - if (++bhe == &buflist[NBUF]) - bhe = buflist; - }; - if (!read) - return -EIO; - filp->f_reada = 1; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return read; -} - -static ssize_t sysv_file_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - off_t pos; - ssize_t written, c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("sysv_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } -/* - * OK, append may not work when many processes are writing at the same time - * but so what. That way leads to madness anyway. - * But we need to protect against simultaneous truncate as we may end up - * writing our data into blocks that have meanwhile been incorporated into - * the freelist, thereby trashing the freelist. - */ - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - written = 0; - while (written<count) { - bh = sysv_getblk (inode, pos >> sb->sv_block_size_bits, 1); - if (!bh) { - if (!written) - written = -ENOSPC; - break; - } - c = sb->sv_block_size - (pos & sb->sv_block_size_1); - if (c > count-written) - c = count-written; - if (c != sb->sv_block_size && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) - written = -EIO; - break; - } - } - /* now either c==sb->sv_block_size or buffer_uptodate(bh) */ - p = (pos & sb->sv_block_size_1) + bh->b_data; - copy_from_user(p, buf, c); - update_vm_cache(inode, pos, p, c); - pos += c; - if (pos > inode->i_size) { - inode->i_size = pos; - mark_inode_dirty(inode); - } - written += c; - buf += c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; -} diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index f8d508c3d..d335b5b50 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -657,7 +657,8 @@ int sysv_bmap(struct inode * inode,int block_nr) /* Access selected blocks of regular files (or directories) */ -static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create) +static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp; @@ -669,31 +670,48 @@ static struct buffer_head * inode_getblk(struct inode * inode, int nr, int creat repeat: tmp = *p; if (tmp) { - result = sv_getblk(sb, inode->i_dev, tmp); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = sysv_new_block(sb); if (!tmp) return NULL; - result = sv_getblk(sb, inode->i_dev, tmp); - if (*p) { - sysv_free_block(sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (*p) { + sysv_free_block(sb, tmp); + brelse(result); + goto repeat; + } + } else { + if (*p) { + sysv_free_block(sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp, block; @@ -717,13 +735,19 @@ repeat: if (sb->sv_convert) block = from_coh_ulong(block); if (tmp) { - result = sv_getblk(sb, bh->b_dev, block); - if (tmp == *p) { + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -734,11 +758,17 @@ repeat: brelse(bh); return NULL; } - result = sv_getblk(sb, bh->b_dev, block); - if (*p) { - sysv_free_block(sb,block); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (*p) { + sysv_free_block(sb,block); + brelse(result); + goto repeat; + } + } else { + *phys_block = tmp; + result = NULL; + *created = 1; } *p = (sb->sv_convert ? to_coh_ulong(block) : block); mark_buffer_dirty(bh, 1); @@ -746,37 +776,74 @@ repeat: return result; } -struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block, int create) +int sysv_getblk_block(struct inode *inode, long block, int create, + int *err, int *created) { - struct super_block * sb = inode->i_sb; - struct buffer_head * bh; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *tmp; + int phys_block; - if (block < 10) - return inode_getblk(inode,block,create); + *err = -EIO; + if (block < 0) { + printk("sysv_getblk: block<0"); + return 0; + } + if (block > sb->sv_ind_per_block_3) { + printk("sysv_getblk: block>big"); + return 0; + } + if (block < 10) { + tmp = inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; + } block -= 10; if (block < sb->sv_ind_per_block) { - bh = inode_getblk(inode,10,create); - return block_getblk(inode, bh, block, create); + bh = inode_getblk(inode, 10, create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block; if (block < sb->sv_ind_per_block_2) { - bh = inode_getblk(inode,11,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); + bh = inode_getblk(inode, 11, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create, + 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block_2; - if (block < sb->sv_ind_per_block_3) { - bh = inode_getblk(inode,12,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create); - bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); - } - if ((int)block<0) { - printk("sysv_getblk: block<0"); - return NULL; + bh = inode_getblk(inode, 12, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create, + 1, NULL, NULL); + bh = block_getblk(inode, bh, + (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, + create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + +out: + *err = 0; + return phys_block; +} + +struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int err, created; + + phys_block = sysv_getblk_block(inode, block, create, &err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); + if (created) { + memset(tmp->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } } - printk("sysv_getblk: block>big"); - return NULL; + return tmp; } struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create) diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index c318648a9..a8c0e0745 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -35,6 +35,9 @@ * general case (size = XXX). I hope. */ +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + /* We throw away any data beyond inode->i_size. */ static int trunc_direct(struct inode * inode) @@ -58,7 +61,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (block != *p)) { + if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) { retry = 1; brelse(bh); continue; @@ -115,7 +118,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (tmp != *ind)) { + if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) { retry = 1; brelse(bh); continue; @@ -128,7 +131,7 @@ repeat: for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -185,7 +188,7 @@ static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -242,7 +245,7 @@ static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 7e94bfd1c..9e027cfc3 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -41,52 +41,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -static long long ufs_file_lseek(struct file *, long long, int); -static ssize_t ufs_file_write (struct file *, const char *, size_t, loff_t *); -static int ufs_release_file (struct inode *, struct file *); - -/* - * We have mostly NULL's here: the current defaults are ok for - * the ufs filesystem. - */ -static struct file_operations ufs_file_operations = { - ufs_file_lseek, /* lseek */ - generic_file_read, /* read */ - ufs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - ufs_release_file, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL /* revalidate */ -}; - -struct inode_operations ufs_file_inode_operations = { - &ufs_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ - ufs_bmap, /* bmap */ - ufs_truncate, /* truncate */ - NULL, /* permission */ - NULL /* smap */ -}; - /* * Make sure the offset never goes beyond the 32-bit mark.. */ @@ -133,139 +87,49 @@ static inline void remove_suid(struct inode *inode) } } -static ssize_t ufs_file_write ( - struct file * filp, - const char * buf, - size_t count, - loff_t *ppos ) +static int ufs_writepage (struct file *file, struct page *page) { - struct inode * inode = filp->f_dentry->d_inode; - __u32 pos; - long block; - int offset; - int written, c; - struct buffer_head * bh, *bufferlist[NBUF]; - struct super_block * sb; - int err; - int i,buffercount,write_error; - - /* POSIX: mtime/ctime may not change for 0 count */ - if (!count) - return 0; - write_error = buffercount = 0; - if (!inode) - return -EINVAL; - sb = inode->i_sb; - if (sb->s_flags & MS_RDONLY) - /* - * This fs has been automatically remounted ro because of errors - */ - return -ENOSPC; - - if (!S_ISREG(inode->i_mode)) { - ufs_warning (sb, "ufs_file_write", "mode = %07o", - inode->i_mode); - return -EINVAL; - } - remove_suid(inode); - - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else { - pos = *ppos; - if (pos != *ppos) - return -EINVAL; - } - - /* Check for overflow.. */ - if (pos > (__u32) (pos + count)) { - count = ~pos; /* == 0xFFFFFFFF - pos */ - if (!count) - return -EFBIG; - } - - /* - * If a file has been opened in synchronous mode, we have to ensure - * that meta-data will also be written synchronously. Thus, we - * set the i_osync field. This field is tested by the allocation - * routines. - */ - if (filp->f_flags & O_SYNC) - inode->u.ufs_i.i_osync++; - block = pos >> sb->s_blocksize_bits; - offset = pos & (sb->s_blocksize - 1); - c = sb->s_blocksize - offset; - written = 0; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/512]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; + block = page->offset >> inode->i_sb->s_blocksize_bits; + p = nr; + bh = page->buffers; do { - bh = ufs_getfrag (inode, block, 1, &err); - if (!bh) { - if (!written) - written = err; - break; - } - if (c > count) - c = count; - if (c != sb->s_blocksize && !buffer_uptodate(bh)) { - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - if (!written) - written = -EIO; - break; - } - } - c -= copy_from_user (bh->b_data + offset, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, bh->b_data + offset, c); - pos += c; - written += c; - buf += c; - count -= c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - if (filp->f_flags & O_SYNC) - bufferlist[buffercount++] = bh; + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; else - brelse(bh); - if (buffercount == NBUF){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - buffercount=0; - } - if (write_error) - break; + *p = ufs_getfrag_block(inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; block++; - offset = 0; - c = sb->s_blocksize; - } while (count); - if (buffercount){ - ll_rw_block(WRITE, buffercount, bufferlist); - for (i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - } - if (pos > inode->i_size) - inode->i_size = pos; - if (filp->f_flags & O_SYNC) - inode->u.ufs_i.i_osync--; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; + p++; + if (bh) + bh = bh->b_this_page; + } while (i > 0); + + brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); + return 0; +} + +static long ufs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, ufs_getfrag_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, ufs_write_one_page); } /* @@ -277,3 +141,47 @@ static int ufs_release_file (struct inode * inode, struct file * filp) { return 0; } + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ufs filesystem. + */ +static struct file_operations ufs_file_operations = { + ufs_file_lseek, /* lseek */ + generic_file_read, /* read */ + ufs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* flush */ + ufs_release_file, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ufs_file_inode_operations = { + &ufs_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + generic_readpage, /* readpage */ + ufs_writepage, /* writepage */ + ufs_bmap, /* bmap */ + ufs_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ +}; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index a5a51bac5..636b0aabd 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -175,7 +175,7 @@ int ufs_bmap (struct inode * inode, int fragment) static struct buffer_head * ufs_inode_getfrag (struct inode * inode, unsigned fragment, unsigned new_fragment, int create, - unsigned required, int * err ) + unsigned required, int *err, int metadata, int *phys_block, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -201,13 +201,19 @@ repeat: tmp = SWAB32(*p); lastfrag = inode->u.ufs_i.i_lastfrag; if (tmp && fragment < lastfrag) { - result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); - if (tmp == SWAB32(*p)) { - UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; + if (metadata) { + result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, + sb->s_blocksize); + if (tmp == SWAB32(*p)) { + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; } - brelse (result); - goto repeat; } *err = -EFBIG; if (!create) @@ -269,7 +275,20 @@ repeat: else return NULL; } - result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); + + /* The nullification of framgents done in ufs/balloc.c is + * something I don't have the stomache to move into here right + * now. -DaveM + */ + if (metadata) { + result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } + inode->i_ctime = CURRENT_TIME; if (IS_SYNC(inode)) ufs_sync_inode (inode); @@ -280,7 +299,7 @@ repeat: static struct buffer_head * ufs_block_getfrag (struct inode * inode, struct buffer_head * bh, unsigned fragment, unsigned new_fragment, - int create, unsigned blocksize, int * err) + int create, unsigned blocksize, int * err, int metadata, int *phys_block, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -312,19 +331,36 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, repeat: tmp = SWAB32(*p); if (tmp) { - result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); - if (tmp == SWAB32(*p)) { + if (metadata) { + result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, + sb->s_blocksize); + if (tmp == SWAB32(*p)) { + brelse (bh); + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; brelse (bh); - UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; + return NULL; } - brelse (result); - goto repeat; } - if (!create || new_fragment >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> sb->s_blocksize)) { + *err = -EFBIG; + if (!create) { brelse (bh); - *err = -EFBIG; return NULL; + } else { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= sb->s_blocksize_bits; + if (new_fragment >= limit) { + brelse (bh); + send_sig(SIGXFSZ, current, 0); + return NULL; + } + } } if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb)) goal = tmp + uspi->s_fpb; @@ -334,12 +370,25 @@ repeat: if (!tmp) { if (SWAB32(*p)) { goto repeat; - } - else { + } else { + brelse (bh); return NULL; } } - result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + + /* The nullification of framgents done in ufs/balloc.c is + * something I don't have the stomache to move into here right + * now. -DaveM + */ + if (metadata) { + result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } + mark_buffer_dirty(bh, 1); if (IS_SYNC(inode)) { ll_rw_block (WRITE, 1, &bh); @@ -352,14 +401,15 @@ repeat: return result; } -struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, - int create, int * err) +int ufs_getfrag_block (struct inode * inode, long fragment, + int create, int * err, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; - struct buffer_head * bh; + struct buffer_head * bh, * tmp; unsigned f; unsigned swab; + int phys_block; sb = inode->i_sb; uspi = sb->u.ufs_sb.s_uspi; @@ -367,19 +417,27 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, *err = -EIO; UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + if (fragment < 0) { + ufs_warning (sb, "ufs_getblk", "block < 0"); + return 0; + } if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { ufs_warning (sb, "ufs_getblk", "block > big"); - return NULL; + return 0; } *err = -ENOSPC; f = fragment; + *created = 0; /* * Direct fragment */ - if (fragment < UFS_NDIR_FRAGMENT) - return ufs_inode_getfrag (inode, fragment, fragment, create, 1, err); + if (fragment < UFS_NDIR_FRAGMENT) { + tmp = ufs_inode_getfrag (inode, fragment, fragment, create, 1, + err, 0, &phys_block, created); + goto out; + } /* * Indirect fragment */ @@ -387,10 +445,12 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { bh = ufs_inode_getfrag (inode, UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift), - f, create, uspi->s_fpb, err); - return ufs_block_getfrag (inode, bh, - fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, uspi->s_fpb, err, 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, + fragment & uspi->s_apbmask, + f, create, sb->s_blocksize, + err, 0, &phys_block, created); + goto out; } /* * Dindirect fragment @@ -398,14 +458,18 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { bh = ufs_inode_getfrag (inode, - UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), - f, create, uspi->s_fpb, err); + UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), + f, create, uspi->s_fpb, err, + 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); - return ufs_block_getfrag (inode, bh, + f, create, sb->s_blocksize, err, + 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, sb->s_blocksize, err, + 0, &phys_block, created); + goto out; } /* * Tindirect fragment @@ -413,19 +477,42 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); bh = ufs_inode_getfrag (inode, UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), - f, create, uspi->s_fpb, err); + f, create, uspi->s_fpb, err, 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, sb->s_blocksize, err, 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); - return ufs_block_getfrag (inode, bh, + f, create, sb->s_blocksize, err, 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); -} + f, create, sb->s_blocksize, err, 0, &phys_block, created); +out: + if (!phys_block) + return 0; + if (*err) + return 0; + return phys_block; +} +struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment, + int create, int *err) +{ + struct buffer_head *tmp = NULL; + int phys_block, created; + + phys_block = ufs_getfrag_block(inode, fragment, create, err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, inode->i_sb->s_blocksize); + if (created) { + memset(tmp->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; +} struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment, int create, int * err) diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 3fec735a2..4649a4253 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -62,6 +62,9 @@ #define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize) #define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize) +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + static int ufs_trunc_direct (struct inode * inode) { struct super_block * sb; @@ -114,7 +117,7 @@ static int ufs_trunc_direct (struct inode * inode) frag2 = ufs_fragnum (frag2); for (j = frag1; j < frag2; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next1; @@ -137,7 +140,7 @@ next1: continue; for (j = 0; j < uspi->s_fpb; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next2; @@ -176,7 +179,7 @@ next2: frag4 = ufs_fragnum (frag4); for (j = 0; j < frag4; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next1; @@ -237,7 +240,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p) continue; for (j = 0; j < uspi->s_fpb; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*ind)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*ind)) { retry = 1; brelse (bh); goto next; diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index a780a9587..0f26103c4 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -838,6 +838,5 @@ struct inode_operations umsdos_dir_inode_operations = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 3f5d10953..7951bb8f8 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -253,6 +253,5 @@ struct inode_operations umsdos_rdir_inode_operations = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 4b3678a22..97ea2da41 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -141,7 +141,6 @@ struct inode_operations umsdos_symlink_inode_operations = NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; |