aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-04-06 09:17:26 -0400
committerDavid Howells <dhowells@redhat.com>2018-04-09 16:54:48 -0400
commit5a8132761609bd7e42db642d6f157140d5bf2ae8 (patch)
tree70230e95b522728ccf37c67bc69b70abc62df003
parent76a5cb6fc1e22a2a316fb690fc4cdd5121d1c0ff (diff)
afs: Do better accretion of small writes on newly created content
Processes like ld that do lots of small writes that aren't necessarily contiguous result in a lot of small StoreData operations to the server, the idea being that if someone else changes the data on the server, we only write our changes over that and not the space between. Further, we don't want to write back empty space if we can avoid it to make it easier for the server to do sparse files. However, making lots of tiny RPC ops is a lot less efficient for the server than one big one because each op requires allocation of resources and the taking of locks, so we want to compromise a bit. Reduce the load by the following: (1) If a file is just created locally or has just been truncated with O_TRUNC locally, allow subsequent writes to the file to be merged with intervening space if that space doesn't cross an entire intervening page. (2) Don't flush the file on ->flush() but rather on ->release() if the file was open for writing. Just linking vmlinux.o, without this patch, looking in /proc/fs/afs/stats: file-wr : n=441 nb=513581204 and after the patch: file-wr : n=62 nb=513668555 there were 379 fewer StoreData RPC operations at the expense of an extra 87K being written. Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--fs/afs/callback.c1
-rw-r--r--fs/afs/dir.c3
-rw-r--r--fs/afs/file.c7
-rw-r--r--fs/afs/internal.h2
-rw-r--r--fs/afs/write.c32
5 files changed, 24 insertions, 21 deletions
diff --git a/fs/afs/callback.c b/fs/afs/callback.c
index 6049ca837498..abd9a84f4e88 100644
--- a/fs/afs/callback.c
+++ b/fs/afs/callback.c
@@ -130,6 +130,7 @@ void afs_break_callback(struct afs_vnode *vnode)
130 130
131 write_seqlock(&vnode->cb_lock); 131 write_seqlock(&vnode->cb_lock);
132 132
133 clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
133 if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { 134 if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
134 vnode->cb_break++; 135 vnode->cb_break++;
135 afs_clear_permits(vnode); 136 afs_clear_permits(vnode);
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 43bb3b23a879..5889f70d4d27 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -1092,6 +1092,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
1092 struct afs_file_status *newstatus, 1092 struct afs_file_status *newstatus,
1093 struct afs_callback *newcb) 1093 struct afs_callback *newcb)
1094{ 1094{
1095 struct afs_vnode *vnode;
1095 struct inode *inode; 1096 struct inode *inode;
1096 1097
1097 if (fc->ac.error < 0) 1098 if (fc->ac.error < 0)
@@ -1109,6 +1110,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
1109 return; 1110 return;
1110 } 1111 }
1111 1112
1113 vnode = AFS_FS_I(inode);
1114 set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
1112 d_add(new_dentry, inode); 1115 d_add(new_dentry, inode);
1113} 1116}
1114 1117
diff --git a/fs/afs/file.c b/fs/afs/file.c
index e5cac1bc3cf0..c24c08016dd9 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -30,7 +30,6 @@ static int afs_readpages(struct file *filp, struct address_space *mapping,
30 30
31const struct file_operations afs_file_operations = { 31const struct file_operations afs_file_operations = {
32 .open = afs_open, 32 .open = afs_open,
33 .flush = afs_flush,
34 .release = afs_release, 33 .release = afs_release,
35 .llseek = generic_file_llseek, 34 .llseek = generic_file_llseek,
36 .read_iter = generic_file_read_iter, 35 .read_iter = generic_file_read_iter,
@@ -146,6 +145,9 @@ int afs_open(struct inode *inode, struct file *file)
146 if (ret < 0) 145 if (ret < 0)
147 goto error_af; 146 goto error_af;
148 } 147 }
148
149 if (file->f_flags & O_TRUNC)
150 set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
149 151
150 file->private_data = af; 152 file->private_data = af;
151 _leave(" = 0"); 153 _leave(" = 0");
@@ -170,6 +172,9 @@ int afs_release(struct inode *inode, struct file *file)
170 172
171 _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); 173 _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
172 174
175 if ((file->f_mode & FMODE_WRITE))
176 return vfs_fsync(file, 0);
177
173 file->private_data = NULL; 178 file->private_data = NULL;
174 if (af->wb) 179 if (af->wb)
175 afs_put_wb_key(af->wb); 180 afs_put_wb_key(af->wb);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index f6b44f47732d..f8086ec95e24 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -506,6 +506,7 @@ struct afs_vnode {
506#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ 506#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */
507#define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */ 507#define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */
508#define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */ 508#define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
509#define AFS_VNODE_NEW_CONTENT 8 /* Set if file has new content (create/trunc-0) */
509 510
510 struct list_head wb_keys; /* List of keys available for writeback */ 511 struct list_head wb_keys; /* List of keys available for writeback */
511 struct list_head pending_locks; /* locks waiting to be granted */ 512 struct list_head pending_locks; /* locks waiting to be granted */
@@ -1026,7 +1027,6 @@ extern int afs_writepage(struct page *, struct writeback_control *);
1026extern int afs_writepages(struct address_space *, struct writeback_control *); 1027extern int afs_writepages(struct address_space *, struct writeback_control *);
1027extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); 1028extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
1028extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); 1029extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
1029extern int afs_flush(struct file *, fl_owner_t);
1030extern int afs_fsync(struct file *, loff_t, loff_t, int); 1030extern int afs_fsync(struct file *, loff_t, loff_t, int);
1031extern int afs_page_mkwrite(struct vm_fault *); 1031extern int afs_page_mkwrite(struct vm_fault *);
1032extern void afs_prune_wb_keys(struct afs_vnode *); 1032extern void afs_prune_wb_keys(struct afs_vnode *);
diff --git a/fs/afs/write.c b/fs/afs/write.c
index eccc16198f68..160f6cc26c00 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -125,7 +125,12 @@ try_again:
125 page->index, priv); 125 page->index, priv);
126 goto flush_conflicting_write; 126 goto flush_conflicting_write;
127 } 127 }
128 if (to < f || from > t) 128 /* If the file is being filled locally, allow inter-write
129 * spaces to be merged into writes. If it's not, only write
130 * back what the user gives us.
131 */
132 if (!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags) &&
133 (to < f || from > t))
129 goto flush_conflicting_write; 134 goto flush_conflicting_write;
130 if (from < f) 135 if (from < f)
131 f = from; 136 f = from;
@@ -419,7 +424,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
419 trace_afs_page_dirty(vnode, tracepoint_string("WARN"), 424 trace_afs_page_dirty(vnode, tracepoint_string("WARN"),
420 primary_page->index, priv); 425 primary_page->index, priv);
421 426
422 if (start >= final_page || to < PAGE_SIZE) 427 if (start >= final_page ||
428 (to < PAGE_SIZE && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)))
423 goto no_more; 429 goto no_more;
424 430
425 start++; 431 start++;
@@ -440,9 +446,10 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
440 } 446 }
441 447
442 for (loop = 0; loop < n; loop++) { 448 for (loop = 0; loop < n; loop++) {
443 if (to != PAGE_SIZE)
444 break;
445 page = pages[loop]; 449 page = pages[loop];
450 if (to != PAGE_SIZE &&
451 !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags))
452 break;
446 if (page->index > final_page) 453 if (page->index > final_page)
447 break; 454 break;
448 if (!trylock_page(page)) 455 if (!trylock_page(page))
@@ -455,7 +462,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
455 priv = page_private(page); 462 priv = page_private(page);
456 f = priv & AFS_PRIV_MAX; 463 f = priv & AFS_PRIV_MAX;
457 t = priv >> AFS_PRIV_SHIFT; 464 t = priv >> AFS_PRIV_SHIFT;
458 if (f != 0) { 465 if (f != 0 &&
466 !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) {
459 unlock_page(page); 467 unlock_page(page);
460 break; 468 break;
461 } 469 }
@@ -741,20 +749,6 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
741} 749}
742 750
743/* 751/*
744 * Flush out all outstanding writes on a file opened for writing when it is
745 * closed.
746 */
747int afs_flush(struct file *file, fl_owner_t id)
748{
749 _enter("");
750
751 if ((file->f_mode & FMODE_WRITE) == 0)
752 return 0;
753
754 return vfs_fsync(file, 0);
755}
756
757/*
758 * notification that a previously read-only page is about to become writable 752 * notification that a previously read-only page is about to become writable
759 * - if it returns an error, the caller will deliver a bus error signal 753 * - if it returns an error, the caller will deliver a bus error signal
760 */ 754 */