summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
commit51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch)
tree5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /fs
parent52273a23c9a84336b93a35e4847fc88fac7eb0e4 (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')
-rw-r--r--fs/Config.in3
-rw-r--r--fs/affs/dir.c1
-rw-r--r--fs/affs/file.c2
-rw-r--r--fs/autofs/dir.c1
-rw-r--r--fs/autofs/root.c1
-rw-r--r--fs/autofs/symlink.c1
-rw-r--r--fs/bad_inode.c4
-rw-r--r--fs/binfmt_aout.c6
-rw-r--r--fs/binfmt_elf.c6
-rw-r--r--fs/block_dev.c1
-rw-r--r--fs/buffer.c848
-rw-r--r--fs/devices.c7
-rw-r--r--fs/devpts/root.c1
-rw-r--r--fs/ext2/balloc.c16
-rw-r--r--fs/ext2/dir.c6
-rw-r--r--fs/ext2/file.c271
-rw-r--r--fs/ext2/fsync.c195
-rw-r--r--fs/ext2/inode.c282
-rw-r--r--fs/ext2/symlink.c6
-rw-r--r--fs/ext2/truncate.c50
-rw-r--r--fs/fifo.c78
-rw-r--r--fs/hfs/dir_nat.c2
-rw-r--r--fs/hfs/file.c1
-rw-r--r--fs/hfs/file_cap.c1
-rw-r--r--fs/hfs/file_hdr.c1
-rw-r--r--fs/hpfs/inode.c3
-rw-r--r--fs/inode.c7
-rw-r--r--fs/isofs/file.c5
-rw-r--r--fs/isofs/inode.c12
-rw-r--r--fs/minix/bitmap.c8
-rw-r--r--fs/minix/file.c119
-rw-r--r--fs/minix/inode.c290
-rw-r--r--fs/minix/truncate.c11
-rw-r--r--fs/msdos/namei.c1
-rw-r--r--fs/ncpfs/dir.c1
-rw-r--r--fs/nfs/dir.c311
-rw-r--r--fs/nfs/file.c10
-rw-r--r--fs/nfs/inode.c35
-rw-r--r--fs/nfs/read.c26
-rw-r--r--fs/nfs/symlink.c56
-rw-r--r--fs/nfs/write.c13
-rw-r--r--fs/ntfs/fs.c3
-rw-r--r--fs/pipe.c92
-rw-r--r--fs/proc/array.c25
-rw-r--r--fs/proc/base.c7
-rw-r--r--fs/proc/fd.c7
-rw-r--r--fs/proc/generic.c38
-rw-r--r--fs/proc/kmsg.c7
-rw-r--r--fs/proc/link.c7
-rw-r--r--fs/proc/mem.c9
-rw-r--r--fs/proc/net.c7
-rw-r--r--fs/proc/omirr.c38
-rw-r--r--fs/proc/proc_devtree.c6
-rw-r--r--fs/proc/root.c48
-rw-r--r--fs/proc/scsi.c37
-rw-r--r--fs/proc/sysvipc.c37
-rw-r--r--fs/read_write.c7
-rw-r--r--fs/smbfs/dir.c1
-rw-r--r--fs/smbfs/file.c7
-rw-r--r--fs/smbfs/inode.c5
-rw-r--r--fs/smbfs/proc.c128
-rw-r--r--fs/super.c15
-rw-r--r--fs/sysv/file.c260
-rw-r--r--fs/sysv/inode.c153
-rw-r--r--fs/sysv/truncate.c13
-rw-r--r--fs/ufs/file.c258
-rw-r--r--fs/ufs/inode.c169
-rw-r--r--fs/ufs/truncate.c11
-rw-r--r--fs/umsdos/dir.c1
-rw-r--r--fs/umsdos/rdir.c1
-rw-r--r--fs/umsdos/symlink.c1
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(&current->sigmask_lock);
flush_signals(current);
spin_unlock_irq(&current->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);
}
diff --git a/fs/fifo.c b/fs/fifo.c
index 5117d3a6e..e18183fc9 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -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 */
};
diff --git a/fs/pipe.c b/fs/pipe.c
index 3283240a9..9830418cc 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -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 */
};