Commit f6506d68 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.3.7pre3

parent 2fcc8e43
...@@ -651,9 +651,7 @@ static inline ssize_t do_tty_write( ...@@ -651,9 +651,7 @@ static inline ssize_t do_tty_write(
ssize_t ret = 0, written = 0; ssize_t ret = 0, written = 0;
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
up(&inode->i_sem); if (down_interruptible(&inode->i_sem)) {
if (down_interruptible(&inode->i_atomic_write)) {
down(&inode->i_sem);
return -ERESTARTSYS; return -ERESTARTSYS;
} }
for (;;) { for (;;) {
...@@ -678,8 +676,7 @@ static inline ssize_t do_tty_write( ...@@ -678,8 +676,7 @@ static inline ssize_t do_tty_write(
file->f_dentry->d_inode->i_mtime = CURRENT_TIME; file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
ret = written; ret = written;
} }
up(&inode->i_atomic_write); up(&inode->i_sem);
down(&inode->i_sem);
return ret; return ret;
} }
......
...@@ -1264,7 +1264,8 @@ static void uhci_interrupt_notify(struct uhci *uhci) ...@@ -1264,7 +1264,8 @@ static void uhci_interrupt_notify(struct uhci *uhci)
struct uhci_qh *interrupt_qh = td->qh; struct uhci_qh *interrupt_qh = td->qh;
usb_dotoggle(td->dev, usb_pipeendpoint(td->info)); usb_dotoggle(td->dev, usb_pipeendpoint(td->info));
td->info |= 1 << 19; /* toggle between data0 and data1 */ td->info &= ~(1 << 19); /* clear data toggle */
td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info)) << 19; /* toggle between data0 and data1 */
td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */
/* Remove then readd? Is that necessary */ /* Remove then readd? Is that necessary */
......
...@@ -109,7 +109,7 @@ union bdflush_param { ...@@ -109,7 +109,7 @@ union bdflush_param {
int dummy3; /* unused */ int dummy3; /* unused */
} b_un; } b_un;
unsigned int data[N_PARAM]; unsigned int data[N_PARAM];
} bdf_prm = {{90, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; } bdf_prm = {{100, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
/* These are the min and max parameter values that we will allow to be assigned */ /* 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_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1};
...@@ -422,6 +422,24 @@ void invalidate_buffers(kdev_t dev) ...@@ -422,6 +422,24 @@ void invalidate_buffers(kdev_t dev)
#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) #define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask)
#define hash(dev,block) hash_table[_hashfn(dev,block)] #define hash(dev,block) hash_table[_hashfn(dev,block)]
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 inline void remove_from_hash_queue(struct buffer_head * bh) static inline void remove_from_hash_queue(struct buffer_head * bh)
{ {
struct buffer_head **pprev = bh->b_pprev; struct buffer_head **pprev = bh->b_pprev;
...@@ -433,16 +451,36 @@ static inline void remove_from_hash_queue(struct buffer_head * bh) ...@@ -433,16 +451,36 @@ static inline void remove_from_hash_queue(struct buffer_head * bh)
} }
*pprev = next; *pprev = next;
bh->b_pprev = NULL; 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)) { struct buffer_head **bhp = &lru_list[bh->b_list];
printk("VFS: LRU block list corrupted\n");
*(int*)0 = 0; 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 inline 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) { if (bh->b_dev == B_FREE) {
printk("LRU list corrupted"); printk("LRU list corrupted");
*(int*)0 = 0; *(int*)0 = 0;
...@@ -455,6 +493,9 @@ static inline void remove_from_lru_list(struct buffer_head * bh) ...@@ -455,6 +493,9 @@ static inline void remove_from_lru_list(struct buffer_head * bh)
if (lru_list[bh->b_list] == bh) if (lru_list[bh->b_list] == bh)
lru_list[bh->b_list] = NULL; lru_list[bh->b_list] = NULL;
bh->b_next_free = bh->b_prev_free = 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 inline void remove_from_free_list(struct buffer_head * bh)
...@@ -479,15 +520,8 @@ static inline void remove_from_free_list(struct buffer_head * bh) ...@@ -479,15 +520,8 @@ static inline void remove_from_free_list(struct buffer_head * bh)
static void remove_from_queues(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 */
goto out;
}
nr_buffers_type[bh->b_list]--;
remove_from_hash_queue(bh); remove_from_hash_queue(bh);
remove_from_lru_list(bh); remove_from_lru_list(bh);
out:
} }
static inline void put_last_free(struct buffer_head * bh) static inline void put_last_free(struct buffer_head * bh)
...@@ -510,69 +544,6 @@ static inline void put_last_free(struct buffer_head * bh) ...@@ -510,69 +544,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++;
}
}
static void insert_into_dirty_queue(struct buffer_head * bh)
{
struct buffer_head **bhp;
bhp = &lru_list[BUF_DIRTY];
if(!*bhp) {
*bhp = bh;
bh->b_prev_free = bh;
}
if (bh->b_next_free)
BUG();
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[BUF_DIRTY]++;
}
struct buffer_head * find_buffer(kdev_t dev, int block, int size) struct buffer_head * find_buffer(kdev_t dev, int block, int size)
{ {
struct buffer_head * next; struct buffer_head * next;
...@@ -673,7 +644,7 @@ void set_blocksize(kdev_t dev, int size) ...@@ -673,7 +644,7 @@ void set_blocksize(kdev_t dev, int size)
} }
remove_from_queues(bh); remove_from_queues(bh);
bh->b_dev=B_FREE; bh->b_dev=B_FREE;
insert_into_queues(bh); put_last_free(bh);
} }
} }
} }
...@@ -693,7 +664,6 @@ static void refill_freelist(int size) ...@@ -693,7 +664,6 @@ static void refill_freelist(int size)
void init_buffer(struct buffer_head *bh, kdev_t dev, int block, void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
bh_end_io_t *handler, void *dev_id) bh_end_io_t *handler, void *dev_id)
{ {
bh->b_count = 1;
bh->b_list = BUF_CLEAN; bh->b_list = BUF_CLEAN;
bh->b_flushtime = 0; bh->b_flushtime = 0;
bh->b_dev = dev; bh->b_dev = dev;
...@@ -743,8 +713,12 @@ struct buffer_head * getblk(kdev_t dev, int block, int size) ...@@ -743,8 +713,12 @@ struct buffer_head * getblk(kdev_t dev, int block, int size)
* and that it's unused (b_count=0), unlocked, and clean. * and that it's unused (b_count=0), unlocked, and clean.
*/ */
init_buffer(bh, dev, block, end_buffer_io_sync, NULL); init_buffer(bh, dev, block, end_buffer_io_sync, NULL);
bh->b_state=0; bh->b_count = 1;
insert_into_queues(bh); bh->b_state = 0;
/* Insert the buffer into the regular lists */
insert_into_lru_list(bh);
insert_into_hash_list(bh);
goto out; goto out;
/* /*
...@@ -781,9 +755,9 @@ void set_writetime(struct buffer_head * buf, int flag) ...@@ -781,9 +755,9 @@ void set_writetime(struct buffer_head * buf, int flag)
*/ */
static 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; bh->b_list = list;
insert_into_queues(bh); insert_into_lru_list(bh);
} }
/* /*
...@@ -813,7 +787,7 @@ static inline void balance_dirty (kdev_t dev) ...@@ -813,7 +787,7 @@ static inline void balance_dirty (kdev_t dev)
* A buffer may need to be moved from one buffer list to another * A buffer may need to be moved from one buffer list to another
* (e.g. in case it is not shared any more). Handle this. * (e.g. in case it is not shared any more). Handle this.
*/ */
void __refile_buffer(struct buffer_head * buf) void refile_buffer(struct buffer_head * buf)
{ {
int dispose; int dispose;
...@@ -829,7 +803,7 @@ void __refile_buffer(struct buffer_head * buf) ...@@ -829,7 +803,7 @@ void __refile_buffer(struct buffer_head * buf)
dispose = BUF_CLEAN; dispose = BUF_CLEAN;
if(dispose != buf->b_list) { if(dispose != buf->b_list) {
file_buffer(buf, dispose); file_buffer(buf, dispose);
if(dispose == BUF_DIRTY) if (dispose == BUF_DIRTY)
balance_dirty(buf->b_dev); balance_dirty(buf->b_dev);
} }
} }
...@@ -1282,89 +1256,143 @@ static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], ...@@ -1282,89 +1256,143 @@ static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[],
} }
tail->b_this_page = head; tail->b_this_page = head;
page->buffers = head; page->buffers = head;
get_page(page);
return 0; return 0;
} }
/* /*
* Can the buffer be thrown out? * 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..
*/ */
#define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected)) int generic_block_flushpage(struct inode *inode, struct page *page, unsigned long offset)
#define buffer_busy(bh) ((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS))
static int page_idle(struct page *page, int sync)
{ {
struct buffer_head *head, *bh, *next; struct buffer_head *head, *bh, *next;
unsigned int curr_off = 0;
if (!PageLocked(page))
BUG();
if (!page->buffers)
return 0;
head = page->buffers; head = page->buffers;
bh = head; bh = head;
do { do {
unsigned int next_off = curr_off + bh->b_size;
next = bh->b_this_page; next = bh->b_this_page;
if (bh->b_blocknr) { /*
if (buffer_locked(bh)) { * is this block fully flushed?
wait_on_buffer(bh); */
return 0; if (offset <= curr_off) {
} if (bh->b_blocknr) {
if (buffer_dirty(bh)) { if (buffer_locked(bh))
if (sync) {
wait_on_buffer(bh); wait_on_buffer(bh);
ll_rw_block(WRITE, 1, &bh); clear_bit(BH_Dirty, &bh->b_state);
return 0; if(bh->b_dev == B_FREE)
} else BUG();
clear_bit(BH_Dirty, &bh->b_state); remove_from_lru_list(bh);
bh->b_blocknr = 0;
} }
} }
curr_off = next_off;
bh = next; bh = next;
} while (bh != head); } while (bh != head);
return 1;
/*
* subtle. We release buffer-heads only if this is
* the 'final' flushpage. We invalidate the bmap
* cached value in all cases.
*/
if (!offset) {
buffermem += PAGE_SIZE;
try_to_free_buffers(page);
}
return 0;
} }
/* static inline void create_empty_buffers (struct page *page,
* We dont have to release all buffers here, but struct inode *inode, unsigned long blocksize)
* we have to be sure that no dirty buffer is left
* and no IO is going on (no buffer is locked), because
* we are going to free the underlying page.
*/
int generic_block_flushpage(struct inode *inode, struct page *page, int sync)
{ {
struct buffer_head *head, *bh, *next; struct buffer_head *bh, *head, *tail;
head = create_buffers(page_address(page), blocksize, 1);
if (page->buffers)
BUG();
bh = head;
do {
bh->b_dev = inode->i_dev;
tail = bh;
bh = bh->b_this_page;
} while (bh);
tail->b_this_page = head;
page->buffers = head;
}
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)) if (!PageLocked(page))
BUG(); BUG();
if (!page->buffers) if (!page->buffers)
BUG(); create_empty_buffers(page, inode, inode->i_sb->s_blocksize);
head = page->buffers;
while (!page_idle(page, sync)); offset = page->offset;
block = offset >> inode->i_sb->s_blocksize_bits;
// FIXME: currently we assume page alignment.
if (offset & (PAGE_SIZE-1))
BUG();
head = page->buffers;
bh = head; bh = head;
i = 0;
do { do {
next = bh->b_this_page; if (!bh)
if (bh->b_blocknr) { BUG();
if(bh->b_dev == B_FREE) {
remove_from_free_list(bh); if (!bh->b_blocknr) {
} else { err = -EIO;
if (bh->b_list == BUF_DIRTY) { down(&inode->i_sem);
nr_buffers--; phys = fs_get_block (inode, block, 1, &err, &created);
nr_buffers_type[BUF_DIRTY]--; up(&inode->i_sem);
remove_from_lru_list(bh); if (!phys)
} goto out;
}
init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL);
bh->b_state = (1<<BH_Uptodate);
} else { } else {
/*
* block already exists, just mark it dirty:
*/
bh->b_end_io = end_buffer_io_sync;
set_bit(BH_Uptodate, &bh->b_state);
} }
bh->b_state = 0; mark_buffer_dirty(bh, 0);
bh->b_count = 0;
put_unused_buffer_head(bh); bh = bh->b_this_page;
bh = next; block++;
} while (bh != head); } while (bh != head);
page->buffers = NULL;
SetPageUptodate(page);
return 0; return 0;
out:
ClearPageUptodate(page);
return err;
} }
int block_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block)
long block_write_one_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 dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -1373,7 +1401,7 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o ...@@ -1373,7 +1401,7 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o
unsigned long blocksize, start_block, end_block; unsigned long blocksize, start_block, end_block;
unsigned long start_offset, start_bytes, end_bytes; unsigned long start_offset, start_bytes, end_bytes;
unsigned long bbits, phys, blocks, i, len; unsigned long bbits, phys, blocks, i, len;
struct buffer_head *bh; struct buffer_head *bh, *head;
char * target_buf; char * target_buf;
target_buf = (char *)page_address(page) + offset; target_buf = (char *)page_address(page) + offset;
...@@ -1383,22 +1411,9 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o ...@@ -1383,22 +1411,9 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o
BUG(); BUG();
blocksize = inode->i_sb->s_blocksize; blocksize = inode->i_sb->s_blocksize;
if (!page->buffers) { if (!page->buffers)
struct buffer_head *head, *tail; create_empty_buffers(page, inode, blocksize);
head = page->buffers;
head = create_buffers(page_address(page), blocksize, 1);
if (page->buffers)
BUG();
bh = head;
do {
bh->b_dev = inode->i_dev;
tail = bh;
bh = bh->b_this_page;
} while (bh);
tail->b_this_page = head;
page->buffers = head;
}
bbits = inode->i_sb->s_blocksize_bits; bbits = inode->i_sb->s_blocksize_bits;
block = page->offset >> bbits; block = page->offset >> bbits;
...@@ -1425,8 +1440,8 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o ...@@ -1425,8 +1440,8 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o
if (page->offset & (PAGE_SIZE-1)) if (page->offset & (PAGE_SIZE-1))
BUG(); BUG();
bh = page->buffers;
i = 0; i = 0;
bh = head;
do { do {
if (!bh) if (!bh)
BUG(); BUG();
...@@ -1434,9 +1449,38 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o ...@@ -1434,9 +1449,38 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o
if ((i < start_block) || (i > end_block)) { if ((i < start_block) || (i > end_block)) {
goto skip; goto skip;
} }
unlock_kernel();
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;
}
if (copy_from_user(target_buf, buf, len))
goto out_nolock;
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.
*/
lock_kernel();
if (!bh->b_blocknr) { if (!bh->b_blocknr) {
phys = fs_get_block (inode, block, 1, &err, &created);
err = -EIO; err = -EIO;
down(&inode->i_sem);
phys = fs_get_block (inode, block, 1, &err, &created);
up(&inode->i_sem);
if (!phys) if (!phys)
goto out; goto out;
...@@ -1458,48 +1502,20 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o ...@@ -1458,48 +1502,20 @@ long block_write_one_page (struct file *file, struct page *page, unsigned long o
lock_kernel(); lock_kernel();
init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL);
bh->b_state = (1<<BH_Dirty) | (1<<BH_Uptodate); bh->b_state = (1<<BH_Uptodate);
bh->b_list = BUF_DIRTY;
insert_into_dirty_queue(bh);
} else { } else {
/* /*
* block already exists, just mark it dirty: * block already exists, just mark it dirty:
*/ */
bh->b_end_io = end_buffer_io_sync; bh->b_end_io = end_buffer_io_sync;
set_bit(BH_Dirty, &bh->b_state);
set_bit(BH_Uptodate, &bh->b_state); set_bit(BH_Uptodate, &bh->b_state);
} }
unlock_kernel(); mark_buffer_dirty(bh, 0);
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;
}
if (copy_from_user(target_buf, buf, len))
goto out_nolock;
target_buf += len;
buf += len;
lock_kernel();
if (bh->b_list != BUF_DIRTY) {
bh->b_list = BUF_DIRTY;
insert_into_dirty_queue(bh);
}
skip: skip:
i++; i++;
block++; block++;
bh = bh->b_this_page; bh = bh->b_this_page;
} while (i < blocks); } while (bh != head);
unlock_kernel(); unlock_kernel();
SetPageUptodate(page); SetPageUptodate(page);
...@@ -1545,7 +1561,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) ...@@ -1545,7 +1561,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
do { do {
block = *(b++); block = *(b++);
if (fresh && (bh->b_count != 1)) if (fresh && (bh->b_count != 0))
BUG(); BUG();
if (rw == READ) { if (rw == READ) {
if (!fresh) if (!fresh)
...@@ -1569,12 +1585,8 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) ...@@ -1569,12 +1585,8 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
if (!block) if (!block)
BUG(); BUG();
} }
set_bit(BH_Dirty, &bh->b_state);
set_bit(BH_Uptodate, &bh->b_state); set_bit(BH_Uptodate, &bh->b_state);
if (bh->b_list != BUF_DIRTY) { mark_buffer_dirty(bh, 0);
bh->b_list = BUF_DIRTY;
insert_into_dirty_queue(bh);
}
arr[nr++] = bh; arr[nr++] = bh;
} }
bh = bh->b_this_page; bh = bh->b_this_page;
...@@ -1701,7 +1713,6 @@ static int grow_buffers(int size) ...@@ -1701,7 +1713,6 @@ static int grow_buffers(int size)
tmp->b_next_free = tmp; tmp->b_next_free = tmp;
} }
insert_point = tmp; insert_point = tmp;
nr_buffers++;
if (tmp->b_this_page) if (tmp->b_this_page)
tmp = tmp->b_this_page; tmp = tmp->b_this_page;
else else
...@@ -1714,6 +1725,12 @@ static int grow_buffers(int size) ...@@ -1714,6 +1725,12 @@ static int grow_buffers(int size)
return 1; return 1;
} }
/*
* 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))
/* /*
* try_to_free_buffers() checks if all the buffers on this particular page * try_to_free_buffers() checks if all the buffers on this particular page
* are unused, and free's the page if so. * are unused, and free's the page if so.
...@@ -1732,6 +1749,13 @@ int try_to_free_buffers(struct page * page) ...@@ -1732,6 +1749,13 @@ int try_to_free_buffers(struct page * page)
tmp = tmp->b_this_page; tmp = tmp->b_this_page;
if (!buffer_busy(p)) if (!buffer_busy(p))
continue; continue;
{
static int count = 30;
if (count) {
count--;
printk("bh %p (%04x:%ld): count=%d, state=0x%04x\n", p, p->b_dev, p->b_blocknr, p->b_count, p->b_state);
}
}
wakeup_bdflush(0); wakeup_bdflush(0);
return 0; return 0;
...@@ -1741,7 +1765,6 @@ int try_to_free_buffers(struct page * page) ...@@ -1741,7 +1765,6 @@ int try_to_free_buffers(struct page * page)
do { do {
struct buffer_head * p = tmp; struct buffer_head * p = tmp;
tmp = tmp->b_this_page; tmp = tmp->b_this_page;
nr_buffers--;
remove_from_queues(p); remove_from_queues(p);
put_unused_buffer_head(p); put_unused_buffer_head(p);
} while (tmp != bh); } while (tmp != bh);
......
...@@ -108,34 +108,7 @@ static inline void remove_suid(struct inode *inode) ...@@ -108,34 +108,7 @@ static inline void remove_suid(struct inode *inode)
static int ext2_writepage (struct file * file, struct page * page) static int ext2_writepage (struct file * file, struct page * page)
{ {
struct dentry *dentry = file->f_dentry; return block_write_full_page(file, page, ext2_getblk_block);
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 {
if (bh && bh->b_blocknr)
*p = bh->b_blocknr;
else
*p = ext2_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->s_blocksize, 1);
return 0;
} }
static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
......
...@@ -130,7 +130,6 @@ static inline void init_once(struct inode * inode) ...@@ -130,7 +130,6 @@ static inline void init_once(struct inode * inode)
INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_dentry); INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1); sema_init(&inode->i_sem, 1);
sema_init(&inode->i_atomic_write, 1);
} }
static inline void write_inode(struct inode *inode) static inline void write_inode(struct inode *inode)
...@@ -767,9 +766,6 @@ kdevname(inode->i_dev), inode->i_ino, inode->i_count); ...@@ -767,9 +766,6 @@ kdevname(inode->i_dev), inode->i_ino, inode->i_count);
if (atomic_read(&inode->i_sem.count) != 1) if (atomic_read(&inode->i_sem.count) != 1)
printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n", 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)); 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 #endif
} }
if (inode->i_count > (1<<31)) { if (inode->i_count > (1<<31)) {
......
...@@ -112,14 +112,6 @@ int minix_new_block(struct super_block * sb) ...@@ -112,14 +112,6 @@ int minix_new_block(struct super_block * sb)
if (j < sb->u.minix_sb.s_firstdatazone || if (j < sb->u.minix_sb.s_firstdatazone ||
j >= sb->u.minix_sb.s_nzones) j >= sb->u.minix_sb.s_nzones)
return 0; 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; return j;
} }
......
...@@ -27,7 +27,51 @@ ...@@ -27,7 +27,51 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/minix_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 * We have mostly NULLs here: the current defaults are OK for
...@@ -61,74 +105,12 @@ struct inode_operations minix_file_inode_operations = { ...@@ -61,74 +105,12 @@ struct inode_operations minix_file_inode_operations = {
NULL, /* readlink */ NULL, /* readlink */
NULL, /* follow_link */ NULL, /* follow_link */
generic_readpage, /* readpage */ generic_readpage, /* readpage */
NULL, /* writepage */ minix_writepage, /* writepage */
minix_bmap, /* bmap */ minix_bmap, /* bmap */
minix_truncate, /* truncate */ minix_truncate, /* truncate */
NULL /* permission */ NULL, /* permission */
NULL, /* smap */
NULL, /* updatepage */
NULL, /* revalidate */
generic_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;
}
...@@ -407,7 +407,7 @@ static int V2_block_bmap(struct buffer_head * bh, int nr) ...@@ -407,7 +407,7 @@ static int V2_block_bmap(struct buffer_head * bh, int nr)
return tmp; return tmp;
} }
static int V2_minix_bmap(struct inode * inode,int block) static int V2_minix_bmap(struct inode * inode, int block)
{ {
int i; int i;
...@@ -454,7 +454,7 @@ static int V2_minix_bmap(struct inode * inode,int block) ...@@ -454,7 +454,7 @@ static int V2_minix_bmap(struct inode * inode,int block)
/* /*
* The global minix fs bmap function. * 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) if (INODE_VERSION(inode) == MINIX_V1)
return V1_minix_bmap(inode, block); return V1_minix_bmap(inode, block);
...@@ -465,8 +465,8 @@ int minix_bmap(struct inode * inode,int block) ...@@ -465,8 +465,8 @@ int minix_bmap(struct inode * inode,int block)
/* /*
* The minix V1 fs getblk functions. * The minix V1 fs getblk functions.
*/ */
static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, int create,
int create) int metadata, int *phys_block, int *created)
{ {
int tmp; int tmp;
unsigned short *p; unsigned short *p;
...@@ -476,31 +476,51 @@ static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, ...@@ -476,31 +476,51 @@ static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr,
repeat: repeat:
tmp = *p; tmp = *p;
if (tmp) { if (tmp) {
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (metadata) {
if (tmp == *p) result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
return result; if (tmp == *p)
brelse(result); return result;
goto repeat; brelse(result);
goto repeat;
} else {
*phys_block = tmp;
return NULL;
}
} }
if (!create) if (!create)
return NULL; return NULL;
tmp = minix_new_block(inode->i_sb); tmp = minix_new_block(inode->i_sb);
if (!tmp) if (!tmp)
return NULL; return NULL;
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (metadata) {
if (*p) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
minix_free_block(inode->i_sb,tmp); if (*p) {
brelse(result); minix_free_block(inode->i_sb, tmp);
goto repeat; 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; *p = tmp;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return result; return result;
} }
static struct buffer_head * V1_block_getblk(struct inode * inode, 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; int tmp;
unsigned short *p; unsigned short *p;
...@@ -520,13 +540,19 @@ static struct buffer_head * V1_block_getblk(struct inode * inode, ...@@ -520,13 +540,19 @@ static struct buffer_head * V1_block_getblk(struct inode * inode,
repeat: repeat:
tmp = *p; tmp = *p;
if (tmp) { if (tmp) {
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (metadata) {
if (tmp == *p) { 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); brelse(bh);
return result; return NULL;
} }
brelse(result);
goto repeat;
} }
if (!create) { if (!create) {
brelse(bh); brelse(bh);
...@@ -537,49 +563,74 @@ static struct buffer_head * V1_block_getblk(struct inode * inode, ...@@ -537,49 +563,74 @@ static struct buffer_head * V1_block_getblk(struct inode * inode,
brelse(bh); brelse(bh);
return NULL; return NULL;
} }
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (metadata) {
if (*p) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
minix_free_block(inode->i_sb,tmp); if (*p) {
brelse(result); minix_free_block(inode->i_sb, tmp);
goto repeat; 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; *p = tmp;
mark_buffer_dirty(bh, 1); mark_buffer_dirty(bh, 1);
brelse(bh); brelse(bh);
return result; return result;
} }
static struct buffer_head * V1_minix_getblk(struct inode * inode, int block, int V1_getblk_block(struct inode * inode, long block, int create, int *err, int *created)
int create)
{ {
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"); printk("minix_getblk: block<0");
return NULL; return 0;
} }
if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
printk("minix_getblk: block>big"); 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; block -= 7;
if (block < 512) { if (block < 512) {
bh = V1_inode_getblk(inode,7,create); bh = V1_inode_getblk(inode, 7, create, 1, NULL, NULL);
return V1_block_getblk(inode, bh, block, create); tmp = V1_block_getblk(inode, bh, block, create,
0, &phys_block, created);
goto out;
} }
block -= 512; block -= 512;
bh = V1_inode_getblk(inode,8,create); bh = V1_inode_getblk(inode, 8, create, 1, NULL, NULL);
bh = V1_block_getblk(inode, bh, (block>>9) & 511, create); bh = V1_block_getblk(inode, bh, (block>>9) & 511, create, 1, NULL, NULL);
return V1_block_getblk(inode, bh, block & 511, create); 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. * The minix V2 fs getblk functions.
*/ */
static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, int create,
int create) int metadata, int *phys_block, int *created)
{ {
int tmp; int tmp;
unsigned long *p; unsigned long *p;
...@@ -589,31 +640,51 @@ static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, ...@@ -589,31 +640,51 @@ static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr,
repeat: repeat:
tmp = *p; tmp = *p;
if (tmp) { if (tmp) {
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (metadata) {
if (tmp == *p) result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
return result; if (tmp == *p)
brelse(result); return result;
goto repeat; brelse(result);
goto repeat;
} else {
*phys_block = tmp;
return NULL;
}
} }
if (!create) if (!create)
return NULL; return NULL;
tmp = minix_new_block(inode->i_sb); tmp = minix_new_block(inode->i_sb);
if (!tmp) if (!tmp)
return NULL; return NULL;
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (metadata) {
if (*p) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
minix_free_block(inode->i_sb,tmp); if (*p) {
brelse(result); minix_free_block(inode->i_sb, tmp);
goto repeat; 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; *p = tmp;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return result; return result;
} }
static struct buffer_head * V2_block_getblk(struct inode * inode, 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; int tmp;
unsigned long *p; unsigned long *p;
...@@ -633,13 +704,19 @@ static struct buffer_head * V2_block_getblk(struct inode * inode, ...@@ -633,13 +704,19 @@ static struct buffer_head * V2_block_getblk(struct inode * inode,
repeat: repeat:
tmp = *p; tmp = *p;
if (tmp) { if (tmp) {
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (metadata) {
if (tmp == *p) { 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); brelse(bh);
return result; return NULL;
} }
brelse(result);
goto repeat;
} }
if (!create) { if (!create) {
brelse(bh); brelse(bh);
...@@ -650,60 +727,107 @@ static struct buffer_head * V2_block_getblk(struct inode * inode, ...@@ -650,60 +727,107 @@ static struct buffer_head * V2_block_getblk(struct inode * inode,
brelse(bh); brelse(bh);
return NULL; return NULL;
} }
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (metadata) {
if (*p) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
minix_free_block(inode->i_sb,tmp); if (*p) {
brelse(result); minix_free_block(inode->i_sb, tmp);
goto repeat; 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; *p = tmp;
mark_buffer_dirty(bh, 1); mark_buffer_dirty(bh, 1);
brelse(bh); brelse(bh);
return result; return result;
} }
static struct buffer_head * V2_minix_getblk(struct inode * inode, int block, int V2_getblk_block(struct inode * inode, int block, int create, int *err, int *created)
int create)
{ {
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"); printk("minix_getblk: block<0");
return NULL; return 0;
} }
if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
printk("minix_getblk: block>big"); 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; block -= 7;
if (block < 256) { if (block < 256) {
bh = V2_inode_getblk(inode,7,create); bh = V2_inode_getblk(inode, 7, create, 1, NULL, NULL);
return V2_block_getblk(inode, bh, block, create); tmp = V2_block_getblk(inode, bh, block, create,
0, &phys_block, created);
goto out;
} }
block -= 256; block -= 256;
if (block < 256*256) { if (block < 256*256) {
bh = V2_inode_getblk(inode,8,create); bh = V2_inode_getblk(inode, 8, create, 1, NULL, NULL);
bh = V2_block_getblk(inode, bh, (block>>8) & 255, create); bh = V2_block_getblk(inode, bh, (block>>8) & 255, create,
return V2_block_getblk(inode, bh, block & 255, create); 1, NULL, NULL);
tmp = V2_block_getblk(inode, bh, block & 255, create,
0, &phys_block, created);
goto out;
} }
block -= 256*256; block -= 256*256;
bh = V2_inode_getblk(inode,9,create); bh = V2_inode_getblk(inode, 9, create, 1, NULL, NULL);
bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create); bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create, 1, NULL, NULL);
bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create); bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create, 1, NULL, NULL);
return V2_block_getblk(inode, bh, block & 255, create); 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. * 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) struct buffer_head *tmp = NULL;
return V1_minix_getblk(inode,block,create); int phys_block;
else int err, created;
return V2_minix_getblk(inode,block,create);
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) struct buffer_head * minix_bread(struct inode * inode, int block, int create)
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
* general case (size = XXX). I hope. * 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. * The functions for minix V1 fs truncation.
*/ */
...@@ -52,7 +55,7 @@ static int V1_trunc_direct(struct inode * inode) ...@@ -52,7 +55,7 @@ static int V1_trunc_direct(struct inode * inode)
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || tmp != *p) { if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
...@@ -103,7 +106,7 @@ static int V1_trunc_indirect(struct inode * inode, int offset, unsigned short * ...@@ -103,7 +106,7 @@ static int V1_trunc_indirect(struct inode * inode, int offset, unsigned short *
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || tmp != *ind) { if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
...@@ -216,7 +219,7 @@ static int V2_trunc_direct(struct inode * inode) ...@@ -216,7 +219,7 @@ static int V2_trunc_direct(struct inode * inode)
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || tmp != *p) { if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
...@@ -267,7 +270,7 @@ static int V2_trunc_indirect(struct inode * inode, int offset, unsigned long * p ...@@ -267,7 +270,7 @@ static int V2_trunc_indirect(struct inode * inode, int offset, unsigned long * p
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || tmp != *ind) { if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
......
...@@ -118,6 +118,61 @@ struct nfs_cookie_table { ...@@ -118,6 +118,61 @@ struct nfs_cookie_table {
}; };
static kmem_cache_t *nfs_cookie_cachep; 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 /* Since a cookie of zero is declared special by the NFS
* protocol, we easily can tell if a cookie in an existing * protocol, we easily can tell if a cookie in an existing
* table chunk is valid or not. * table chunk is valid or not.
...@@ -148,38 +203,7 @@ static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off) ...@@ -148,38 +203,7 @@ static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
return ret; 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) #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) static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
{ {
struct nfs_cookie_table **cpp; struct nfs_cookie_table **cpp;
...@@ -211,28 +235,37 @@ static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode) ...@@ -211,28 +235,37 @@ static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
return 0; 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 /* Recover from a revalidation flush. The case here is that
* the inode for the directory got invalidated somehow, and * the inode for the directory got invalidated somehow, and
* all of our cached information is lost. In order to get * all of our cached information is lost. In order to get
* a correct cookie for the current readdir request from the * a correct cookie for the current readdir request from the
* user, we must (re-)fetch older readdir page cache entries. * 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)
{ {
struct page *page; struct page *page;
u32 cur_off, goal_off = off & PAGE_MASK; u32 goal_cookie = file->f_pos;
long cur_off, ret = -1L;
again: again:
cur_off = 0; cur_off = 0;
while (cur_off < goal_off) { for (;;) {
page = find_get_page(inode, cur_off); page = find_get_page(inode, cur_off);
if (page) { if (page) {
if (!Page_Uptodate(page)) if (!Page_Uptodate(page))
goto out_error; goto out_error;
} else { } 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 (!page) {
if (!cur_off) if (!cur_off)
goto out_error; goto out_error;
...@@ -243,17 +276,33 @@ static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 of ...@@ -243,17 +276,33 @@ static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 of
} }
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;
} }
return 0; out:
return ret;
out_error: out_error:
if (page) if (page)
page_cache_release(page); page_cache_release(page);
return -1; 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_readdirargs rd_args;
struct nfs_readdirres rd_res; struct nfs_readdirres rd_res;
...@@ -261,6 +310,7 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs ...@@ -261,6 +310,7 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct page *page, **hash; struct page *page, **hash;
unsigned long page_cache; unsigned long page_cache;
long offset;
__u32 *cookiep; __u32 *cookiep;
page = NULL; page = NULL;
...@@ -268,10 +318,19 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs ...@@ -268,10 +318,19 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs
if (!page_cache) if (!page_cache)
goto out; goto out;
while ((cookiep = find_cookie(inode, offset)) == NULL) { if ((offset = nfs_readdir_offset(inode, cookie)) < 0) {
if (!refetch_ok || 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; goto out;
}
}
cookiep = find_cookie(inode, offset);
if (!cookiep) {
/* Gross fatal error. */
page_cache_free(page_cache);
goto out;
} }
hash = page_hash(inode, offset); hash = page_hash(inode, offset);
...@@ -302,8 +361,7 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs ...@@ -302,8 +361,7 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs
} while(rd_res.bufsiz > 0); } while(rd_res.bufsiz > 0);
if (rd_res.bufsiz < 0) if (rd_res.bufsiz < 0)
NFS_DIREOF(inode) = NFS_DIREOF(inode) = rd_res.cookie;
(offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz);
else if (create_cookie(rd_res.cookie, offset, inode)) else if (create_cookie(rd_res.cookie, offset, inode))
goto error; goto error;
...@@ -318,31 +376,35 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs ...@@ -318,31 +376,35 @@ static struct page *try_to_get_dirent_page(struct file *file, unsigned long offs
goto unlock_out; 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) void *dirent, filldir_t filldir)
{ {
u32 end; u32 end;
if (doff & ~PAGE_CACHE_MASK) {
doff = find_midpoint(p, doff);
p += (doff & ~PAGE_CACHE_MASK) >> 2;
}
while((end = *p++) != 0) { while((end = *p++) != 0) {
__u32 fileid = *p++; __u32 fileid, len, skip, this_cookie;
__u32 len = *p++; char *name;
__u32 skip = NFS_NAMELEN_ALIGN(len);
char *name = (char *) p; fileid = *p++;
len = *p++;
/* Skip the cookie. */ name = (char *) p;
p = ((__u32 *) (name + skip)) + 1; skip = NFS_NAMELEN_ALIGN(len);
if (filldir(dirent, name, len, doff, fileid) < 0) p += (skip >> 2);
goto out; this_cookie = *p++;
doff += (skip + (4 * sizeof(__u32)));
if (this_cookie < cookie)
continue;
cookie = this_cookie;
if (filldir(dirent, name, len, cookie, fileid) < 0)
break;
} }
if (!*p)
doff = PAGE_CACHE_ALIGN(doff); return cookie;
out:
return doff;
} }
/* The file offset position is represented in pure bytes, to /* The file offset position is represented in pure bytes, to
...@@ -357,7 +419,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -357,7 +419,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct dentry *dentry = filp->f_dentry; struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct page *page, **hash; struct page *page, **hash;
unsigned long offset; long offset;
int res; int res;
res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
...@@ -367,7 +429,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -367,7 +429,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode)) if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
return 0; 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); hash = page_hash(inode, offset);
page = __find_get_page(inode, offset, *hash); page = __find_get_page(inode, offset, *hash);
if (!page) if (!page)
...@@ -381,7 +445,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -381,7 +445,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
return 0; return 0;
no_dirent_page: 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) if (!page)
goto no_page; goto no_page;
...@@ -393,20 +457,39 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -393,20 +457,39 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
return -EIO; return -EIO;
} }
/* Invalidate directory cookie caches and EOF marker /* Flush directory cookie and EOF caches for an inode.
* 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); struct nfs_cookie_table *p = NFS_COOKIES(inode);
if (p != NULL) { while (p != NULL) {
NFS_COOKIES(inode) = NULL; struct nfs_cookie_table *next = p->next;
do { struct nfs_cookie_table *next = p->next; kmem_cache_free(nfs_cookie_cachep, p);
kmem_cache_free(nfs_cookie_cachep, p); p = next;
p = next;
} while (p != NULL);
} }
NFS_COOKIES(inode) = NULL;
NFS_DIREOF(inode) = 0; NFS_DIREOF(inode) = 0;
} }
...@@ -532,11 +615,11 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) ...@@ -532,11 +615,11 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
/* Purge readdir caches. */ /* Purge readdir caches. */
if (dentry->d_parent->d_inode) { if (dentry->d_parent->d_inode) {
invalidate_inode_pages(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)) { if (inode && S_ISDIR(inode->i_mode)) {
invalidate_inode_pages(inode); invalidate_inode_pages(inode);
nfs_invalidate_dircache(inode); nfs_flush_dircache(inode);
} }
return 0; return 0;
} }
...@@ -733,7 +816,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) ...@@ -733,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 the dir cache before the operation to avoid a race.
*/ */
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr); dentry->d_name.name, &sattr, &fhandle, &fattr);
if (!error) if (!error)
...@@ -763,7 +846,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde ...@@ -763,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; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr); dentry->d_name.name, &sattr, &fhandle, &fattr);
if (!error) if (!error)
...@@ -798,7 +881,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -798,7 +881,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
*/ */
d_drop(dentry); d_drop(dentry);
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr); dentry->d_name.name, &sattr, &fhandle, &fattr);
return error; return error;
...@@ -819,7 +902,7 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink); ...@@ -819,7 +902,7 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink);
#endif #endif
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name); dentry->d_name.name);
...@@ -947,7 +1030,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -947,7 +1030,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
} while(sdentry->d_inode != NULL); /* need negative lookup */ } while(sdentry->d_inode != NULL); /* need negative lookup */
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_rename(NFS_SERVER(dir), error = nfs_proc_rename(NFS_SERVER(dir),
NFS_FH(dentry->d_parent), dentry->d_name.name, NFS_FH(dentry->d_parent), dentry->d_name.name,
NFS_FH(dentry->d_parent), silly); NFS_FH(dentry->d_parent), silly);
...@@ -1017,7 +1100,7 @@ inode->i_count, inode->i_nlink); ...@@ -1017,7 +1100,7 @@ inode->i_count, inode->i_nlink);
d_delete(dentry); d_delete(dentry);
} }
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent), error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name); dentry->d_name.name);
/* /*
...@@ -1084,7 +1167,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1084,7 +1167,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
*/ */
d_drop(dentry); d_drop(dentry);
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent), error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &sattr); dentry->d_name.name, symname, &sattr);
if (!error) { if (!error) {
...@@ -1115,7 +1198,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) ...@@ -1115,7 +1198,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
*/ */
d_drop(dentry); d_drop(dentry);
invalidate_inode_pages(dir); invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir); nfs_flush_dircache(dir);
error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry), error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
NFS_FH(dentry->d_parent), dentry->d_name.name); NFS_FH(dentry->d_parent), dentry->d_name.name);
if (!error) { if (!error) {
...@@ -1261,9 +1344,9 @@ new_inode->i_count, new_inode->i_nlink); ...@@ -1261,9 +1344,9 @@ new_inode->i_count, new_inode->i_nlink);
} }
invalidate_inode_pages(new_dir); invalidate_inode_pages(new_dir);
nfs_invalidate_dircache(new_dir); nfs_flush_dircache(new_dir);
invalidate_inode_pages(old_dir); invalidate_inode_pages(old_dir);
nfs_invalidate_dircache(old_dir); nfs_flush_dircache(old_dir);
error = nfs_proc_rename(NFS_DSERVER(old_dentry), error = nfs_proc_rename(NFS_DSERVER(old_dentry),
NFS_FH(old_dentry->d_parent), old_dentry->d_name.name, NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
NFS_FH(new_dentry->d_parent), new_dentry->d_name.name); NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
......
...@@ -99,23 +99,28 @@ nfs_delete_inode(struct inode * inode) ...@@ -99,23 +99,28 @@ nfs_delete_inode(struct inode * inode)
int failed; int failed;
dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
/*
* Flush out any pending write requests ... if (S_ISDIR(inode->i_mode)) {
*/ nfs_free_dircache(inode);
if (NFS_WRITEBACK(inode) != NULL) { } else {
unsigned long timeout = jiffies + 5*HZ; /*
* Flush out any pending write requests ...
*/
if (NFS_WRITEBACK(inode) != NULL) {
unsigned long timeout = jiffies + 5*HZ;
#ifdef NFS_DEBUG_VERBOSE #ifdef NFS_DEBUG_VERBOSE
printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
#endif #endif
nfs_inval(inode); nfs_inval(inode);
while (NFS_WRITEBACK(inode) != NULL && while (NFS_WRITEBACK(inode) != NULL &&
time_before(jiffies, timeout)) { time_before(jiffies, timeout)) {
current->state = TASK_INTERRUPTIBLE; current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/10); 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); failed = nfs_check_failed_request(inode);
...@@ -433,7 +438,7 @@ nfs_zap_caches(struct inode *inode) ...@@ -433,7 +438,7 @@ nfs_zap_caches(struct inode *inode)
invalidate_inode_pages(inode); invalidate_inode_pages(inode);
if (S_ISDIR(inode->i_mode)) 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) ...@@ -477,8 +482,6 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_size = fattr->size; inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds; inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds;
NFS_COOKIES(inode) = NULL;
NFS_WRITEBACK(inode) = NULL;
} }
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
} }
......
...@@ -102,9 +102,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf, ...@@ -102,9 +102,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf,
free = count; free = count;
else else
free = 1; /* can't do it atomically, wait for any free space */ free = 1; /* can't do it atomically, wait for any free space */
up(&inode->i_sem); if (down_interruptible(&inode->i_sem)) {
if (down_interruptible(&inode->i_atomic_write)) {
down(&inode->i_sem);
return -ERESTARTSYS; return -ERESTARTSYS;
} }
while (count>0) { while (count>0) {
...@@ -145,8 +143,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf, ...@@ -145,8 +143,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf,
inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_ctime = inode->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
errout: errout:
up(&inode->i_atomic_write); up(&inode->i_sem);
down(&inode->i_sem);
return written ? written : err; return written ? written : err;
} }
...@@ -254,6 +251,7 @@ static int pipe_release(struct inode * inode) ...@@ -254,6 +251,7 @@ static int pipe_release(struct inode * inode)
inode->i_pipe = NULL; inode->i_pipe = NULL;
free_page((unsigned long) info->base); free_page((unsigned long) info->base);
kfree(info); kfree(info);
return 0;
} }
wake_up_interruptible(&PIPE_WAIT(*inode)); wake_up_interruptible(&PIPE_WAIT(*inode));
return 0; return 0;
......
...@@ -166,9 +166,7 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) ...@@ -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)) if (!file->f_op || !(write = file->f_op->write))
goto out; goto out;
down(&inode->i_sem);
ret = write(file, buf, count, &file->f_pos); ret = write(file, buf, count, &file->f_pos);
up(&inode->i_sem);
out: out:
fput(file); fput(file);
bad_file: bad_file:
...@@ -304,9 +302,7 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, ...@@ -304,9 +302,7 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
if (!file) if (!file)
goto bad_file; goto bad_file;
if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) { 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); ret = do_readv_writev(VERIFY_READ, file, vector, count);
up(&file->f_dentry->d_inode->i_sem);
} }
fput(file); fput(file);
...@@ -376,10 +372,7 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, ...@@ -376,10 +372,7 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
if (pos < 0) if (pos < 0)
goto out; goto out;
down(&file->f_dentry->d_inode->i_sem);
ret = write(file, buf, count, &pos); ret = write(file, buf, count, &pos);
up(&file->f_dentry->d_inode->i_sem);
out: out:
fput(file); fput(file);
bad_file: bad_file:
......
...@@ -33,7 +33,51 @@ ...@@ -33,7 +33,51 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sysv_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 * 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 *); ...@@ -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 = { static struct file_operations sysv_file_operations = {
NULL, /* lseek - default */ NULL, /* lseek - default */
sysv_file_read, /* read */ generic_file_read, /* read */
sysv_file_write, /* write */ sysv_file_write, /* write */
NULL, /* readdir - bad */ NULL, /* readdir - bad */
NULL, /* poll - default */ NULL, /* poll - default */
...@@ -50,7 +94,10 @@ static struct file_operations sysv_file_operations = { ...@@ -50,7 +94,10 @@ static struct file_operations sysv_file_operations = {
NULL, /* no special open is needed */ NULL, /* no special open is needed */
NULL, /* flush */ NULL, /* flush */
NULL, /* release */ 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 = { struct inode_operations sysv_file_inode_operations = {
...@@ -67,208 +114,12 @@ struct inode_operations sysv_file_inode_operations = { ...@@ -67,208 +114,12 @@ struct inode_operations sysv_file_inode_operations = {
NULL, /* readlink */ NULL, /* readlink */
NULL, /* follow_link */ NULL, /* follow_link */
generic_readpage, /* readpage */ generic_readpage, /* readpage */
NULL, /* writepage */ sysv_writepage, /* writepage */
sysv_bmap, /* bmap */ sysv_bmap, /* bmap */
sysv_truncate, /* truncate */ sysv_truncate, /* truncate */
NULL /* permission */ NULL, /* permission */
NULL, /* smap */
NULL, /* updatepage */
NULL, /* revalidate */
generic_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;
}
...@@ -657,7 +657,8 @@ int sysv_bmap(struct inode * inode,int block_nr) ...@@ -657,7 +657,8 @@ int sysv_bmap(struct inode * inode,int block_nr)
/* Access selected blocks of regular files (or directories) */ /* 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; struct super_block *sb;
u32 tmp; u32 tmp;
...@@ -669,31 +670,48 @@ static struct buffer_head * inode_getblk(struct inode * inode, int nr, int creat ...@@ -669,31 +670,48 @@ static struct buffer_head * inode_getblk(struct inode * inode, int nr, int creat
repeat: repeat:
tmp = *p; tmp = *p;
if (tmp) { if (tmp) {
result = sv_getblk(sb, inode->i_dev, tmp); if (metadata) {
if (tmp == *p) result = sv_getblk(sb, inode->i_dev, tmp);
return result; if (tmp == *p)
brelse(result); return result;
goto repeat; brelse(result);
goto repeat;
} else {
*phys_block = tmp;
return NULL;
}
} }
if (!create) if (!create)
return NULL; return NULL;
tmp = sysv_new_block(sb); tmp = sysv_new_block(sb);
if (!tmp) if (!tmp)
return NULL; return NULL;
result = sv_getblk(sb, inode->i_dev, tmp); if (metadata) {
if (*p) { result = sv_getblk(sb, inode->i_dev, tmp);
sysv_free_block(sb,tmp); if (*p) {
brelse(result); sysv_free_block(sb, tmp);
goto repeat; brelse(result);
goto repeat;
}
} else {
if (*p) {
sysv_free_block(sb, tmp);
goto repeat;
}
*phys_block = tmp;
result = NULL;
*created = 1;
} }
*p = tmp; *p = tmp;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return result; return result;
} }
static struct buffer_head * block_getblk(struct inode * inode, 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; struct super_block *sb;
u32 tmp, block; u32 tmp, block;
...@@ -717,13 +735,19 @@ static struct buffer_head * block_getblk(struct inode * inode, ...@@ -717,13 +735,19 @@ static struct buffer_head * block_getblk(struct inode * inode,
if (sb->sv_convert) if (sb->sv_convert)
block = from_coh_ulong(block); block = from_coh_ulong(block);
if (tmp) { if (tmp) {
result = sv_getblk(sb, bh->b_dev, block); if (metadata) {
if (tmp == *p) { 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); brelse(bh);
return result; return NULL;
} }
brelse(result);
goto repeat;
} }
if (!create) { if (!create) {
brelse(bh); brelse(bh);
...@@ -734,11 +758,17 @@ static struct buffer_head * block_getblk(struct inode * inode, ...@@ -734,11 +758,17 @@ static struct buffer_head * block_getblk(struct inode * inode,
brelse(bh); brelse(bh);
return NULL; return NULL;
} }
result = sv_getblk(sb, bh->b_dev, block); if (metadata) {
if (*p) { result = sv_getblk(sb, bh->b_dev, block);
sysv_free_block(sb,block); if (*p) {
brelse(result); sysv_free_block(sb,block);
goto repeat; brelse(result);
goto repeat;
}
} else {
*phys_block = tmp;
result = NULL;
*created = 1;
} }
*p = (sb->sv_convert ? to_coh_ulong(block) : block); *p = (sb->sv_convert ? to_coh_ulong(block) : block);
mark_buffer_dirty(bh, 1); mark_buffer_dirty(bh, 1);
...@@ -746,37 +776,74 @@ static struct buffer_head * block_getblk(struct inode * inode, ...@@ -746,37 +776,74 @@ static struct buffer_head * block_getblk(struct inode * inode,
return result; 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 super_block *sb = inode->i_sb;
struct buffer_head * bh; struct buffer_head *bh, *tmp;
int phys_block;
if (block < 10) *err = -EIO;
return inode_getblk(inode,block,create); 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; block -= 10;
if (block < sb->sv_ind_per_block) { if (block < sb->sv_ind_per_block) {
bh = inode_getblk(inode,10,create); bh = inode_getblk(inode, 10, create, 1, NULL, NULL);
return block_getblk(inode, bh, block, create); tmp = block_getblk(inode, bh, block, create,
0, &phys_block, created);
goto out;
} }
block -= sb->sv_ind_per_block; block -= sb->sv_ind_per_block;
if (block < sb->sv_ind_per_block_2) { if (block < sb->sv_ind_per_block_2) {
bh = inode_getblk(inode,11,create); bh = inode_getblk(inode, 11, create, 1, NULL, NULL);
bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, 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); 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; block -= sb->sv_ind_per_block_2;
if (block < sb->sv_ind_per_block_3) { bh = inode_getblk(inode, 12, create, 1, NULL, NULL);
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_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); bh = block_getblk(inode, bh,
return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,
} create, 1, NULL, NULL);
if ((int)block<0) { tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create,
printk("sysv_getblk: block<0"); 0, &phys_block, created);
return NULL;
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 tmp;
return NULL;
} }
struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create) struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create)
......
...@@ -35,6 +35,9 @@ ...@@ -35,6 +35,9 @@
* general case (size = XXX). I hope. * 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. */ /* We throw away any data beyond inode->i_size. */
static int trunc_direct(struct inode * inode) static int trunc_direct(struct inode * inode)
...@@ -58,7 +61,7 @@ static int trunc_direct(struct inode * inode) ...@@ -58,7 +61,7 @@ static int trunc_direct(struct inode * inode)
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || (block != *p)) { if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
...@@ -115,7 +118,7 @@ static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_ ...@@ -115,7 +118,7 @@ static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_
brelse(bh); brelse(bh);
goto repeat; goto repeat;
} }
if ((bh && bh->b_count != 1) || (tmp != *ind)) { if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) {
retry = 1; retry = 1;
brelse(bh); brelse(bh);
continue; continue;
...@@ -128,7 +131,7 @@ static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_ ...@@ -128,7 +131,7 @@ static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_
for (i = 0; i < sb->sv_ind_per_block; i++) for (i = 0; i < sb->sv_ind_per_block; i++)
if (((sysv_zone_t *) indbh->b_data)[i]) if (((sysv_zone_t *) indbh->b_data)[i])
goto done; goto done;
if ((indbh->b_count != 1) || (indtmp != *p)) { if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
brelse(indbh); brelse(indbh);
return 1; return 1;
} }
...@@ -185,7 +188,7 @@ static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone ...@@ -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++) for (i = 0; i < sb->sv_ind_per_block; i++)
if (((sysv_zone_t *) indbh->b_data)[i]) if (((sysv_zone_t *) indbh->b_data)[i])
goto done; goto done;
if ((indbh->b_count != 1) || (indtmp != *p)) { if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
brelse(indbh); brelse(indbh);
return 1; return 1;
} }
...@@ -242,7 +245,7 @@ static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone ...@@ -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++) for (i = 0; i < sb->sv_ind_per_block; i++)
if (((sysv_zone_t *) indbh->b_data)[i]) if (((sysv_zone_t *) indbh->b_data)[i])
goto done; goto done;
if ((indbh->b_count != 1) || (indtmp != *p)) { if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
brelse(indbh); brelse(indbh);
return 1; return 1;
} }
......
...@@ -41,52 +41,6 @@ ...@@ -41,52 +41,6 @@
#define MIN(a,b) (((a)<(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(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.. * Make sure the offset never goes beyond the 32-bit mark..
*/ */
...@@ -133,139 +87,49 @@ static inline void remove_suid(struct inode *inode) ...@@ -133,139 +87,49 @@ static inline void remove_suid(struct inode *inode)
} }
} }
static ssize_t ufs_file_write ( static int ufs_writepage (struct file *file, struct page *page)
struct file * filp,
const char * buf,
size_t count,
loff_t *ppos )
{ {
struct inode * inode = filp->f_dentry->d_inode; struct dentry *dentry = file->f_dentry;
__u32 pos; struct inode *inode = dentry->d_inode;
long block; unsigned long block;
int offset; int *p, nr[PAGE_SIZE/512];
int written, c; int i, err, created;
struct buffer_head * bh, *bufferlist[NBUF]; struct buffer_head *bh;
struct super_block * sb;
int err; i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
int i,buffercount,write_error; block = page->offset >> inode->i_sb->s_blocksize_bits;
p = nr;
/* POSIX: mtime/ctime may not change for 0 count */ bh = page->buffers;
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;
do { do {
bh = ufs_getfrag (inode, block, 1, &err); if (bh && bh->b_blocknr)
if (!bh) { *p = bh->b_blocknr;
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 else
brelse(bh); *p = ufs_getfrag_block(inode, block, 1, &err, &created);
if (buffercount == NBUF){ if (!*p)
ll_rw_block(WRITE, buffercount, bufferlist); return -EIO;
for(i=0; i<buffercount; i++){ i--;
wait_on_buffer(bufferlist[i]);
if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
buffercount=0;
}
if (write_error)
break;
block++; block++;
offset = 0; p++;
c = sb->s_blocksize; if (bh)
} while (count); bh = bh->b_this_page;
if (buffercount){ } while (i > 0);
ll_rw_block(WRITE, buffercount, bufferlist);
for (i=0; i<buffercount; i++){ brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
wait_on_buffer(bufferlist[i]); return 0;
if (!buffer_uptodate(bufferlist[i])) }
write_error=1;
brelse(bufferlist[i]); 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);
if (pos > inode->i_size) }
inode->i_size = pos;
if (filp->f_flags & O_SYNC) /*
inode->u.ufs_i.i_osync--; * Write to a file (through the page cache).
inode->i_ctime = inode->i_mtime = CURRENT_TIME; */
*ppos = pos; static ssize_t
mark_inode_dirty(inode); ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return written; {
return generic_file_write(file, buf, count, ppos, ufs_write_one_page);
} }
/* /*
...@@ -277,3 +141,48 @@ static int ufs_release_file (struct inode * inode, struct file * filp) ...@@ -277,3 +141,48 @@ static int ufs_release_file (struct inode * inode, struct file * filp)
{ {
return 0; 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, /* updatepage */
NULL, /* revalidate */
generic_block_flushpage,/* flushpage */
};
...@@ -175,7 +175,7 @@ int ufs_bmap (struct inode * inode, int fragment) ...@@ -175,7 +175,7 @@ int ufs_bmap (struct inode * inode, int fragment)
static struct buffer_head * ufs_inode_getfrag (struct inode * inode, static struct buffer_head * ufs_inode_getfrag (struct inode * inode,
unsigned fragment, unsigned new_fragment, int create, 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 super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
...@@ -201,13 +201,19 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode, ...@@ -201,13 +201,19 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode,
tmp = SWAB32(*p); tmp = SWAB32(*p);
lastfrag = inode->u.ufs_i.i_lastfrag; lastfrag = inode->u.ufs_i.i_lastfrag;
if (tmp && fragment < lastfrag) { if (tmp && fragment < lastfrag) {
result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); if (metadata) {
if (tmp == SWAB32(*p)) { result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff,
UFSD(("EXIT, result %u\n", tmp + blockoff)) sb->s_blocksize);
return result; 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; *err = -EFBIG;
if (!create) if (!create)
...@@ -269,7 +275,20 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode, ...@@ -269,7 +275,20 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode,
else else
return NULL; 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; inode->i_ctime = CURRENT_TIME;
if (IS_SYNC(inode)) if (IS_SYNC(inode))
ufs_sync_inode (inode); ufs_sync_inode (inode);
...@@ -280,7 +299,7 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode, ...@@ -280,7 +299,7 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode,
static struct buffer_head * ufs_block_getfrag (struct inode * inode, static struct buffer_head * ufs_block_getfrag (struct inode * inode,
struct buffer_head * bh, unsigned fragment, unsigned new_fragment, 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 super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
...@@ -312,19 +331,36 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, ...@@ -312,19 +331,36 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode,
repeat: repeat:
tmp = SWAB32(*p); tmp = SWAB32(*p);
if (tmp) { if (tmp) {
result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); if (metadata) {
if (tmp == SWAB32(*p)) { 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); brelse (bh);
UFSD(("EXIT, result %u\n", tmp + blockoff)) return NULL;
return result;
} }
brelse (result);
goto repeat;
} }
if (!create || new_fragment >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> sb->s_blocksize)) { *err = -EFBIG;
if (!create) {
brelse (bh); brelse (bh);
*err = -EFBIG;
return NULL; 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)) if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb))
goal = tmp + uspi->s_fpb; goal = tmp + uspi->s_fpb;
...@@ -334,12 +370,25 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, ...@@ -334,12 +370,25 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode,
if (!tmp) { if (!tmp) {
if (SWAB32(*p)) { if (SWAB32(*p)) {
goto repeat; goto repeat;
} } else {
else { brelse (bh);
return NULL; 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); mark_buffer_dirty(bh, 1);
if (IS_SYNC(inode)) { if (IS_SYNC(inode)) {
ll_rw_block (WRITE, 1, &bh); ll_rw_block (WRITE, 1, &bh);
...@@ -352,14 +401,15 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, ...@@ -352,14 +401,15 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode,
return result; return result;
} }
struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, int ufs_getfrag_block (struct inode * inode, long fragment,
int create, int * err) int create, int * err, int *created)
{ {
struct super_block * sb; struct super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
struct buffer_head * bh; struct buffer_head * bh, * tmp;
unsigned f; unsigned f;
unsigned swab; unsigned swab;
int phys_block;
sb = inode->i_sb; sb = inode->i_sb;
uspi = sb->u.ufs_sb.s_uspi; uspi = sb->u.ufs_sb.s_uspi;
...@@ -367,19 +417,27 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, ...@@ -367,19 +417,27 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment,
*err = -EIO; *err = -EIO;
UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) 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)) { if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) {
ufs_warning (sb, "ufs_getblk", "block > big"); ufs_warning (sb, "ufs_getblk", "block > big");
return NULL; return 0;
} }
*err = -ENOSPC; *err = -ENOSPC;
f = fragment; f = fragment;
*created = 0;
/* /*
* Direct fragment * Direct fragment
*/ */
if (fragment < UFS_NDIR_FRAGMENT) if (fragment < UFS_NDIR_FRAGMENT) {
return ufs_inode_getfrag (inode, fragment, fragment, create, 1, err); tmp = ufs_inode_getfrag (inode, fragment, fragment, create, 1,
err, 0, &phys_block, created);
goto out;
}
/* /*
* Indirect fragment * Indirect fragment
*/ */
...@@ -387,10 +445,12 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, ...@@ -387,10 +445,12 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment,
if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) {
bh = ufs_inode_getfrag (inode, bh = ufs_inode_getfrag (inode,
UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift), UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift),
f, create, uspi->s_fpb, err); f, create, uspi->s_fpb, err, 1, NULL, NULL);
return ufs_block_getfrag (inode, bh, tmp = ufs_block_getfrag (inode, bh,
fragment & uspi->s_apbmask, fragment & uspi->s_apbmask,
f, create, sb->s_blocksize, err); f, create, sb->s_blocksize,
err, 0, &phys_block, created);
goto out;
} }
/* /*
* Dindirect fragment * Dindirect fragment
...@@ -398,14 +458,18 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, ...@@ -398,14 +458,18 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment,
fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift);
if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) {
bh = ufs_inode_getfrag (inode, bh = ufs_inode_getfrag (inode,
UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift),
f, create, uspi->s_fpb, err); f, create, uspi->s_fpb, err,
1, NULL, NULL);
bh = ufs_block_getfrag (inode, bh, bh = ufs_block_getfrag (inode, bh,
(fragment >> uspi->s_apbshift) & uspi->s_apbmask, (fragment >> uspi->s_apbshift) & uspi->s_apbmask,
f, create, sb->s_blocksize, err); f, create, sb->s_blocksize, err,
return ufs_block_getfrag (inode, bh, 1, NULL, NULL);
tmp = ufs_block_getfrag (inode, bh,
fragment & uspi->s_apbmask, fragment & uspi->s_apbmask,
f, create, sb->s_blocksize, err); f, create, sb->s_blocksize, err,
0, &phys_block, created);
goto out;
} }
/* /*
* Tindirect fragment * Tindirect fragment
...@@ -413,19 +477,42 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, ...@@ -413,19 +477,42 @@ struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment,
fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift);
bh = ufs_inode_getfrag (inode, bh = ufs_inode_getfrag (inode,
UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), 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, bh = ufs_block_getfrag (inode, bh,
(fragment >> uspi->s_2apbshift) & uspi->s_apbmask, (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, bh = ufs_block_getfrag (inode, bh,
(fragment >> uspi->s_apbshift) & uspi->s_apbmask, (fragment >> uspi->s_apbshift) & uspi->s_apbmask,
f, create, sb->s_blocksize, err); f, create, sb->s_blocksize, err, 1, NULL, NULL);
return ufs_block_getfrag (inode, bh, tmp = ufs_block_getfrag (inode, bh,
fragment & uspi->s_apbmask, 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, struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment,
int create, int * err) int create, int * err)
......
...@@ -62,6 +62,9 @@ ...@@ -62,6 +62,9 @@
#define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize) #define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize)
#define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize) #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) static int ufs_trunc_direct (struct inode * inode)
{ {
struct super_block * sb; struct super_block * sb;
...@@ -114,7 +117,7 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -114,7 +117,7 @@ static int ufs_trunc_direct (struct inode * inode)
frag2 = ufs_fragnum (frag2); frag2 = ufs_fragnum (frag2);
for (j = frag1; j < frag2; j++) { for (j = frag1; j < frag2; j++) {
bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); 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; retry = 1;
brelse (bh); brelse (bh);
goto next1; goto next1;
...@@ -137,7 +140,7 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -137,7 +140,7 @@ static int ufs_trunc_direct (struct inode * inode)
continue; continue;
for (j = 0; j < uspi->s_fpb; j++) { for (j = 0; j < uspi->s_fpb; j++) {
bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); 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; retry = 1;
brelse (bh); brelse (bh);
goto next2; goto next2;
...@@ -176,7 +179,7 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -176,7 +179,7 @@ static int ufs_trunc_direct (struct inode * inode)
frag4 = ufs_fragnum (frag4); frag4 = ufs_fragnum (frag4);
for (j = 0; j < frag4; j++) { for (j = 0; j < frag4; j++) {
bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); 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; retry = 1;
brelse (bh); brelse (bh);
goto next1; goto next1;
...@@ -237,7 +240,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p) ...@@ -237,7 +240,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p)
continue; continue;
for (j = 0; j < uspi->s_fpb; j++) { for (j = 0; j < uspi->s_fpb; j++) {
bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); 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; retry = 1;
brelse (bh); brelse (bh);
goto next; goto next;
......
...@@ -356,7 +356,6 @@ struct inode { ...@@ -356,7 +356,6 @@ struct inode {
unsigned long i_version; unsigned long i_version;
unsigned long i_nrpages; unsigned long i_nrpages;
struct semaphore i_sem; struct semaphore i_sem;
struct semaphore i_atomic_write;
struct inode_operations *i_op; struct inode_operations *i_op;
struct super_block *i_sb; struct super_block *i_sb;
wait_queue_head_t i_wait; wait_queue_head_t i_wait;
...@@ -622,7 +621,7 @@ struct inode_operations { ...@@ -622,7 +621,7 @@ struct inode_operations {
int (*smap) (struct inode *,int); int (*smap) (struct inode *,int);
int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int); int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int);
int (*revalidate) (struct dentry *); int (*revalidate) (struct dentry *);
int (*flushpage) (struct inode *, struct page *, int); int (*flushpage) (struct inode *, struct page *, unsigned long);
}; };
struct super_operations { struct super_operations {
...@@ -750,15 +749,7 @@ extern struct file *inuse_filps; ...@@ -750,15 +749,7 @@ extern struct file *inuse_filps;
extern void set_writetime(struct buffer_head *, int); extern void set_writetime(struct buffer_head *, int);
extern int try_to_free_buffers(struct page *); extern int try_to_free_buffers(struct page *);
extern void __refile_buffer(struct buffer_head * buf); extern void refile_buffer(struct buffer_head * buf);
extern inline void refile_buffer(struct buffer_head * buf)
{
/*
* Subtle, we do not want to refile not hashed buffers ...
*/
if (buf->b_pprev)
__refile_buffer(buf);
}
extern int buffermem; extern int buffermem;
...@@ -881,8 +872,10 @@ extern int generic_readpage(struct file *, struct page *); ...@@ -881,8 +872,10 @@ extern int generic_readpage(struct file *, struct page *);
extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int generic_file_mmap(struct file *, struct vm_area_struct *);
extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t); extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t);
extern int generic_block_flushpage(struct inode *, struct page *, int); extern int generic_block_flushpage(struct inode *, struct page *, unsigned long);
extern long block_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block); extern int block_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block);
extern int block_write_full_page (struct file *file, struct page *page, fs_getblock_t fs_get_block);
extern struct super_block *get_super(kdev_t); extern struct super_block *get_super(kdev_t);
extern void put_super(kdev_t); extern void put_super(kdev_t);
......
...@@ -110,6 +110,7 @@ extern unsigned long minix_count_free_blocks(struct super_block *sb); ...@@ -110,6 +110,7 @@ extern unsigned long minix_count_free_blocks(struct super_block *sb);
extern int minix_bmap(struct inode *,int); extern int minix_bmap(struct inode *,int);
extern struct buffer_head * minix_getblk(struct inode *, int, int); extern struct buffer_head * minix_getblk(struct inode *, int, int);
extern int minix_getblk_block (struct inode *, long, int, int *, int *);
extern struct buffer_head * minix_bread(struct inode *, int, int); extern struct buffer_head * minix_bread(struct inode *, int, int);
extern void minix_truncate(struct inode *); extern void minix_truncate(struct inode *);
......
...@@ -192,7 +192,8 @@ extern struct inode_operations nfs_file_inode_operations; ...@@ -192,7 +192,8 @@ extern struct inode_operations nfs_file_inode_operations;
*/ */
extern struct inode_operations nfs_dir_inode_operations; extern struct inode_operations nfs_dir_inode_operations;
extern struct dentry_operations nfs_dentry_operations; extern struct dentry_operations nfs_dentry_operations;
extern void nfs_invalidate_dircache(struct inode *); extern void nfs_flush_dircache(struct inode *);
extern void nfs_free_dircache(struct inode *);
/* /*
* linux/fs/nfs/symlink.c * linux/fs/nfs/symlink.c
......
...@@ -387,6 +387,7 @@ extern unsigned long sysv_count_free_blocks(struct super_block *sb); ...@@ -387,6 +387,7 @@ extern unsigned long sysv_count_free_blocks(struct super_block *sb);
extern int sysv_bmap(struct inode *,int); extern int sysv_bmap(struct inode *,int);
extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int); extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int);
extern int sysv_getblk_block(struct inode *, long, int, int *, int *);
extern struct buffer_head * sysv_file_bread(struct inode *, int, int); extern struct buffer_head * sysv_file_bread(struct inode *, int, int);
extern ssize_t sysv_file_read(struct file *, char *, size_t, loff_t *); extern ssize_t sysv_file_read(struct file *, char *, size_t, loff_t *);
......
...@@ -537,6 +537,7 @@ extern int ufs_sync_inode (struct inode *); ...@@ -537,6 +537,7 @@ extern int ufs_sync_inode (struct inode *);
extern void ufs_write_inode (struct inode *); extern void ufs_write_inode (struct inode *);
extern void ufs_delete_inode (struct inode *); extern void ufs_delete_inode (struct inode *);
extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *);
extern int ufs_getfrag_block (struct inode *, long, int, int *, int *);
extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
/* namei.c */ /* namei.c */
......
...@@ -137,8 +137,6 @@ void truncate_inode_pages(struct inode * inode, unsigned long start) ...@@ -137,8 +137,6 @@ void truncate_inode_pages(struct inode * inode, unsigned long start)
page_cache_release(page); page_cache_release(page);
goto repeat; goto repeat;
} }
if (page_count(page) != 2)
printk("hm, busy page truncated? (not necesserily a bug)\n");
spin_unlock(&pagecache_lock); spin_unlock(&pagecache_lock);
if (inode->i_op->flushpage) if (inode->i_op->flushpage)
...@@ -160,9 +158,6 @@ void truncate_inode_pages(struct inode * inode, unsigned long start) ...@@ -160,9 +158,6 @@ void truncate_inode_pages(struct inode * inode, unsigned long start)
page->prev = NULL; page->prev = NULL;
remove_page_from_hash_queue(page); remove_page_from_hash_queue(page);
page->inode = NULL; page->inode = NULL;
if (page_count(page) != 2)
printk("hm, busy page truncated? (not necesserily a bug)\n");
spin_unlock(&pagecache_lock); spin_unlock(&pagecache_lock);
UnlockPage(page); UnlockPage(page);
...@@ -189,6 +184,13 @@ void truncate_inode_pages(struct inode * inode, unsigned long start) ...@@ -189,6 +184,13 @@ void truncate_inode_pages(struct inode * inode, unsigned long start)
/* partial truncate, clear end of page */ /* partial truncate, clear end of page */
if (offset < PAGE_CACHE_SIZE) { if (offset < PAGE_CACHE_SIZE) {
unsigned long address; unsigned long address;
get_page(page);
if (TryLockPage(page)) {
spin_unlock(&pagecache_lock);
wait_on_page(page);
page_cache_release(page);
goto repeat;
}
/* /*
* It's worth dropping the write lock only at * It's worth dropping the write lock only at
* this point. We are holding the page lock * this point. We are holding the page lock
...@@ -200,10 +202,15 @@ void truncate_inode_pages(struct inode * inode, unsigned long start) ...@@ -200,10 +202,15 @@ void truncate_inode_pages(struct inode * inode, unsigned long start)
address = page_address(page); address = page_address(page);
memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset); memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset);
flush_page_to_ram(address); flush_page_to_ram(address);
if (inode->i_op->flushpage)
inode->i_op->flushpage(inode, page, offset);
/* /*
* we have dropped the lock so we have to * we have dropped the spinlock so we have to
* restart. * restart.
*/ */
UnlockPage(page);
page_cache_release(page);
goto repeat; goto repeat;
} }
} }
...@@ -217,25 +224,9 @@ void truncate_inode_pages(struct inode * inode, unsigned long start) ...@@ -217,25 +224,9 @@ void truncate_inode_pages(struct inode * inode, unsigned long start)
*/ */
void remove_inode_page(struct page *page) void remove_inode_page(struct page *page)
{ {
struct inode *inode = page->inode;
if (!PageLocked(page)) if (!PageLocked(page))
PAGE_BUG(page); PAGE_BUG(page);
/*
* We might sleep here. Other processes might arrive and sleep on
* the lock, but nobody is allowed to 'cross' the lock and get a
* reference to the page. We then remove the page from the hash
* before unlocking it. This mechanizm ensures that 1) nobody gets
* a half-freed page 2) nobody creates the same pagecache content
* before we finish destroying this page. This is not a
* performance problem as pages here are candidates for getting
* freed, ie. it's supposed to be unlikely that the above situation
* happens.
*/
if (inode->i_op->flushpage)
inode->i_op->flushpage(inode, page, 1);
spin_lock(&pagecache_lock); spin_lock(&pagecache_lock);
remove_page_from_inode_queue(page); remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page); remove_page_from_hash_queue(page);
...@@ -274,12 +265,27 @@ int shrink_mmap(int priority, int gfp_mask) ...@@ -274,12 +265,27 @@ int shrink_mmap(int priority, int gfp_mask)
referenced = test_and_clear_bit(PG_referenced, &page->flags); referenced = test_and_clear_bit(PG_referenced, &page->flags);
if (PageLocked(page)) if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
continue; continue;
if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) if (PageLocked(page))
continue; continue;
/* Is it a buffer page? */
if (page->buffers) {
if (buffer_under_min())
continue;
if (TryLockPage(page))
continue;
err = try_to_free_buffers(page);
UnlockPage(page);
if (!err)
continue;
goto out;
}
/* We can't free pages unless there's just one user */ /* We can't free pages unless there's just one user */
if (page_count(page) != 1) if (page_count(page) != 1)
continue; continue;
...@@ -309,13 +315,14 @@ int shrink_mmap(int priority, int gfp_mask) ...@@ -309,13 +315,14 @@ int shrink_mmap(int priority, int gfp_mask)
goto unlock_continue; goto unlock_continue;
if (TryLockPage(page)) if (TryLockPage(page))
goto unlock_continue; goto unlock_continue;
if (page_count(page) != 1) {
UnlockPage(page); if (page_count(page) == 1) {
goto unlock_continue; remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page);
page->inode = NULL;
} }
spin_unlock(&pagecache_lock); spin_unlock(&pagecache_lock);
remove_inode_page(page);
UnlockPage(page); UnlockPage(page);
page_cache_release(page); page_cache_release(page);
err = 1; err = 1;
...@@ -325,17 +332,6 @@ int shrink_mmap(int priority, int gfp_mask) ...@@ -325,17 +332,6 @@ int shrink_mmap(int priority, int gfp_mask)
continue; continue;
} }
spin_unlock(&pagecache_lock); spin_unlock(&pagecache_lock);
/* Is it a buffer page? */
if (page->buffers) {
if (buffer_under_min())
continue;
if (!try_to_free_buffers(page))
continue;
err = 1;
goto out;
}
} while (count > 0); } while (count > 0);
err = 0; err = 0;
out: out:
...@@ -1086,17 +1082,14 @@ static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned ...@@ -1086,17 +1082,14 @@ static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned
ssize_t written; ssize_t written;
unsigned long count = desc->count; unsigned long count = desc->count;
struct file *file = (struct file *) desc->buf; struct file *file = (struct file *) desc->buf;
struct inode *inode = file->f_dentry->d_inode;
mm_segment_t old_fs; mm_segment_t old_fs;
if (size > count) if (size > count)
size = count; size = count;
down(&inode->i_sem);
old_fs = get_fs(); old_fs = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
written = file->f_op->write(file, area, size, &file->f_pos); written = file->f_op->write(file, area, size, &file->f_pos);
set_fs(old_fs); set_fs(old_fs);
up(&inode->i_sem);
if (written < 0) { if (written < 0) {
desc->error = written; desc->error = written;
written = 0; written = 0;
...@@ -1362,7 +1355,6 @@ static inline int do_write_page(struct inode * inode, struct file * file, ...@@ -1362,7 +1355,6 @@ static inline int do_write_page(struct inode * inode, struct file * file,
int retval; int retval;
unsigned long size; unsigned long size;
loff_t loff = offset; loff_t loff = offset;
mm_segment_t old_fs;
int (*writepage) (struct file *, struct page *); int (*writepage) (struct file *, struct page *);
struct page * page; struct page * page;
...@@ -1376,8 +1368,6 @@ static inline int do_write_page(struct inode * inode, struct file * file, ...@@ -1376,8 +1368,6 @@ static inline int do_write_page(struct inode * inode, struct file * file,
return -EIO; return -EIO;
} }
size -= offset; size -= offset;
old_fs = get_fs();
set_fs(KERNEL_DS);
retval = -EIO; retval = -EIO;
writepage = inode->i_op->writepage; writepage = inode->i_op->writepage;
page = mem_map + MAP_NR(page_addr); page = mem_map + MAP_NR(page_addr);
...@@ -1386,11 +1376,13 @@ static inline int do_write_page(struct inode * inode, struct file * file, ...@@ -1386,11 +1376,13 @@ static inline int do_write_page(struct inode * inode, struct file * file,
if (writepage) { if (writepage) {
retval = writepage(file, page); retval = writepage(file, page);
} else { } else {
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
if (size == file->f_op->write(file, page_addr, size, &loff)) if (size == file->f_op->write(file, page_addr, size, &loff))
retval = 0; retval = 0;
set_fs(old_fs);
} }
UnlockPage(page); UnlockPage(page);
set_fs(old_fs);
return retval; return retval;
} }
...@@ -1426,9 +1418,7 @@ static int filemap_write_page(struct vm_area_struct * vma, ...@@ -1426,9 +1418,7 @@ static int filemap_write_page(struct vm_area_struct * vma,
return 0; return 0;
} }
down(&inode->i_sem);
result = do_write_page(inode, file, (const char *) page, offset); result = do_write_page(inode, file, (const char *) page, offset);
up(&inode->i_sem);
fput(file); fput(file);
return result; return result;
} }
...@@ -1642,10 +1632,7 @@ static int msync_interval(struct vm_area_struct * vma, ...@@ -1642,10 +1632,7 @@ static int msync_interval(struct vm_area_struct * vma,
struct file * file = vma->vm_file; struct file * file = vma->vm_file;
if (file) { if (file) {
struct dentry * dentry = file->f_dentry; struct dentry * dentry = file->f_dentry;
struct inode * inode = dentry->d_inode;
down(&inode->i_sem);
error = file_fsync(file, dentry); error = file_fsync(file, dentry);
up(&inode->i_sem);
} }
} }
return error; return error;
...@@ -1972,10 +1959,8 @@ int kpiod(void * unused) ...@@ -1972,10 +1959,8 @@ int kpiod(void * unused)
dentry = p->file->f_dentry; dentry = p->file->f_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
down(&inode->i_sem);
do_write_page(inode, p->file, do_write_page(inode, p->file,
(const char *) p->page, p->offset); (const char *) p->page, p->offset);
up(&inode->i_sem);
fput(p->file); fput(p->file);
page_cache_free(p->page); page_cache_free(p->page);
kmem_cache_free(pio_request_cache, p); kmem_cache_free(pio_request_cache, p);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment