diff options
author | Steve French <sfrench@us.ibm.com> | 2005-10-05 17:50:29 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2005-10-05 17:50:29 -0400 |
commit | 37c0eb4677f733a773df6287b0f73f00274402e3 (patch) | |
tree | 29d6da2f609bdc22316b24aca866ad2ee2093959 /fs/cifs/file.c | |
parent | 6148a742b2bd76abfe0c1fc50dd747cb9f28cd6b (diff) |
CIFS: implement cifs_writepages to perform multi-page I/O
Signed-off-by: Dave Kleikamp <shaggy@austin.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r-- | fs/cifs/file.c | 191 |
1 files changed, 184 insertions, 7 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 94875455d7fa..0473b221f643 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -21,11 +21,14 @@ | |||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
22 | */ | 22 | */ |
23 | #include <linux/fs.h> | 23 | #include <linux/fs.h> |
24 | #include <linux/backing-dev.h> | ||
24 | #include <linux/stat.h> | 25 | #include <linux/stat.h> |
25 | #include <linux/fcntl.h> | 26 | #include <linux/fcntl.h> |
27 | #include <linux/mpage.h> | ||
26 | #include <linux/pagemap.h> | 28 | #include <linux/pagemap.h> |
27 | #include <linux/pagevec.h> | 29 | #include <linux/pagevec.h> |
28 | #include <linux/smp_lock.h> | 30 | #include <linux/smp_lock.h> |
31 | #include <linux/writeback.h> | ||
29 | #include <asm/div64.h> | 32 | #include <asm/div64.h> |
30 | #include "cifsfs.h" | 33 | #include "cifsfs.h" |
31 | #include "cifspdu.h" | 34 | #include "cifspdu.h" |
@@ -916,6 +919,16 @@ static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) | |||
916 | ((open_file->pfile->f_flags & O_RDWR) || | 919 | ((open_file->pfile->f_flags & O_RDWR) || |
917 | (open_file->pfile->f_flags & O_WRONLY))) { | 920 | (open_file->pfile->f_flags & O_WRONLY))) { |
918 | read_unlock(&GlobalSMBSeslock); | 921 | read_unlock(&GlobalSMBSeslock); |
922 | if(open_file->invalidHandle) { | ||
923 | rc = cifs_reopen_file(cifs_inode->vfs_inode, | ||
924 | open_file->pfile, FALSE); | ||
925 | /* if it fails, try another handle - might be */ | ||
926 | /* dangerous to hold up writepages with retry */ | ||
927 | if(rc) { | ||
928 | read_lock(&GlobalSMBSeslock); | ||
929 | continue; | ||
930 | } | ||
931 | } | ||
919 | return open_file; | 932 | return open_file; |
920 | } | 933 | } |
921 | } | 934 | } |
@@ -982,20 +995,181 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) | |||
982 | return rc; | 995 | return rc; |
983 | } | 996 | } |
984 | 997 | ||
985 | #if 0 | 998 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
986 | static int cifs_writepages(struct address_space *mapping, | 999 | static int cifs_writepages(struct address_space *mapping, |
987 | struct writeback_control *wbc) | 1000 | struct writeback_control *wbc) |
988 | { | 1001 | { |
989 | int rc = -EFAULT; | 1002 | struct backing_dev_info *bdi = mapping->backing_dev_info; |
1003 | unsigned int bytes_to_write; | ||
1004 | unsigned int bytes_written; | ||
1005 | struct cifs_sb_info *cifs_sb; | ||
1006 | int done = 0; | ||
1007 | pgoff_t end = -1; | ||
1008 | pgoff_t index; | ||
1009 | int is_range = 0; | ||
1010 | struct kvec iov[32]; | ||
1011 | int n_iov = 0; | ||
1012 | pgoff_t next; | ||
1013 | int nr_pages; | ||
1014 | __u64 offset = 0; | ||
1015 | struct cifsFileInfo *open_file = NULL; | ||
1016 | struct page *page; | ||
1017 | struct pagevec pvec; | ||
1018 | int rc = 0; | ||
1019 | int scanned = 0; | ||
990 | int xid; | 1020 | int xid; |
991 | 1021 | ||
1022 | cifs_sb = CIFS_SB(mapping->host->i_sb); | ||
1023 | |||
1024 | /* | ||
1025 | * If wsize is smaller that the page cache size, default to writing | ||
1026 | * one page at a time via cifs_writepage | ||
1027 | */ | ||
1028 | if (cifs_sb->wsize < PAGE_CACHE_SIZE) | ||
1029 | return generic_writepages(mapping, wbc); | ||
1030 | |||
1031 | /* | ||
1032 | * BB: Is this meaningful for a non-block-device file system? | ||
1033 | * If it is, we should test it again after we do I/O | ||
1034 | */ | ||
1035 | if (wbc->nonblocking && bdi_write_congested(bdi)) { | ||
1036 | wbc->encountered_congestion = 1; | ||
1037 | return 0; | ||
1038 | } | ||
1039 | |||
992 | xid = GetXid(); | 1040 | xid = GetXid(); |
993 | 1041 | ||
994 | /* Find contiguous pages then iterate through repeating | 1042 | pagevec_init(&pvec, 0); |
995 | call 16K write then Setpageuptodate or if LARGE_WRITE_X | 1043 | if (wbc->sync_mode == WB_SYNC_NONE) |
996 | support then send larger writes via kevec so as to eliminate | 1044 | index = mapping->writeback_index; /* Start from prev offset */ |
997 | a memcpy */ | 1045 | else { |
1046 | index = 0; | ||
1047 | scanned = 1; | ||
1048 | } | ||
1049 | if (wbc->start || wbc->end) { | ||
1050 | index = wbc->start >> PAGE_CACHE_SHIFT; | ||
1051 | end = wbc->end >> PAGE_CACHE_SHIFT; | ||
1052 | is_range = 1; | ||
1053 | scanned = 1; | ||
1054 | } | ||
1055 | retry: | ||
1056 | while (!done && (index <= end) && | ||
1057 | (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, | ||
1058 | PAGECACHE_TAG_DIRTY, | ||
1059 | min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) { | ||
1060 | int first; | ||
1061 | unsigned int i; | ||
1062 | |||
1063 | if (!open_file) { | ||
1064 | open_file = find_writable_file(CIFS_I(mapping->host)); | ||
1065 | if (!open_file) { | ||
1066 | pagevec_release(&pvec); | ||
1067 | cERROR(1, ("No writable handles for inode")); | ||
1068 | return -EIO; | ||
1069 | } | ||
1070 | } | ||
1071 | |||
1072 | first = -1; | ||
1073 | next = 0; | ||
1074 | n_iov = 0; | ||
1075 | bytes_to_write = 0; | ||
1076 | |||
1077 | for (i = 0; i < nr_pages; i++) { | ||
1078 | page = pvec.pages[i]; | ||
1079 | /* | ||
1080 | * At this point we hold neither mapping->tree_lock nor | ||
1081 | * lock on the page itself: the page may be truncated or | ||
1082 | * invalidated (changing page->mapping to NULL), or even | ||
1083 | * swizzled back from swapper_space to tmpfs file | ||
1084 | * mapping | ||
1085 | */ | ||
1086 | |||
1087 | if (first < 0) | ||
1088 | lock_page(page); | ||
1089 | else if (TestSetPageLocked(page)) | ||
1090 | break; | ||
1091 | |||
1092 | if (unlikely(page->mapping != mapping)) { | ||
1093 | unlock_page(page); | ||
1094 | break; | ||
1095 | } | ||
1096 | |||
1097 | if (unlikely(is_range) && (page->index > end)) { | ||
1098 | done = 1; | ||
1099 | unlock_page(page); | ||
1100 | break; | ||
1101 | } | ||
1102 | |||
1103 | if (next && (page->index != next)) { | ||
1104 | /* Not next consecutive page */ | ||
1105 | unlock_page(page); | ||
1106 | break; | ||
1107 | } | ||
1108 | |||
1109 | if (wbc->sync_mode != WB_SYNC_NONE) | ||
1110 | wait_on_page_writeback(page); | ||
1111 | |||
1112 | if (PageWriteback(page) || | ||
1113 | !test_clear_page_dirty(page)) { | ||
1114 | unlock_page(page); | ||
1115 | break; | ||
1116 | } | ||
1117 | /* | ||
1118 | * BB can we get rid of this? pages are held by pvec | ||
1119 | */ | ||
1120 | page_cache_get(page); | ||
1121 | |||
1122 | /* reserve iov[0] for the smb header */ | ||
1123 | n_iov++; | ||
1124 | iov[n_iov].iov_base = kmap(page); | ||
1125 | iov[n_iov].iov_len = PAGE_CACHE_SIZE; | ||
1126 | bytes_to_write += PAGE_CACHE_SIZE; | ||
1127 | |||
1128 | if (first < 0) { | ||
1129 | first = i; | ||
1130 | offset = page_offset(page); | ||
1131 | } | ||
1132 | next = page->index + 1; | ||
1133 | if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) | ||
1134 | break; | ||
1135 | } | ||
1136 | if (n_iov) { | ||
1137 | rc = CIFSSMBWrite2(xid, cifs_sb->tcon, | ||
1138 | open_file->netfid, bytes_to_write, | ||
1139 | offset, &bytes_written, iov, n_iov, | ||
1140 | 1); | ||
1141 | if (rc || bytes_written < bytes_to_write) { | ||
1142 | cERROR(1,("CIFSSMBWrite2 returned %d, written = %x", | ||
1143 | rc, bytes_written)); | ||
1144 | set_bit(AS_EIO, &mapping->flags); | ||
1145 | SetPageError(page); | ||
1146 | } | ||
1147 | for (i = 0; i < n_iov; i++) { | ||
1148 | page = pvec.pages[first + i]; | ||
1149 | kunmap(page); | ||
1150 | unlock_page(page); | ||
1151 | page_cache_release(page); | ||
1152 | } | ||
1153 | if ((wbc->nr_to_write -= n_iov) <= 0) | ||
1154 | done = 1; | ||
1155 | index = next; | ||
1156 | } | ||
1157 | pagevec_release(&pvec); | ||
1158 | } | ||
1159 | if (!scanned && !done) { | ||
1160 | /* | ||
1161 | * We hit the last page and there is more work to be done: wrap | ||
1162 | * back to the start of the file | ||
1163 | */ | ||
1164 | scanned = 1; | ||
1165 | index = 0; | ||
1166 | goto retry; | ||
1167 | } | ||
1168 | if (!is_range) | ||
1169 | mapping->writeback_index = index; | ||
1170 | |||
998 | FreeXid(xid); | 1171 | FreeXid(xid); |
1172 | |||
999 | return rc; | 1173 | return rc; |
1000 | } | 1174 | } |
1001 | #endif | 1175 | #endif |
@@ -1635,6 +1809,9 @@ struct address_space_operations cifs_addr_ops = { | |||
1635 | .readpage = cifs_readpage, | 1809 | .readpage = cifs_readpage, |
1636 | .readpages = cifs_readpages, | 1810 | .readpages = cifs_readpages, |
1637 | .writepage = cifs_writepage, | 1811 | .writepage = cifs_writepage, |
1812 | #ifdef CONFIG_CIFS_EXPERIMENTAL | ||
1813 | .writepages = cifs_writepages, | ||
1814 | #endif | ||
1638 | .prepare_write = cifs_prepare_write, | 1815 | .prepare_write = cifs_prepare_write, |
1639 | .commit_write = cifs_commit_write, | 1816 | .commit_write = cifs_commit_write, |
1640 | .set_page_dirty = __set_page_dirty_nobuffers, | 1817 | .set_page_dirty = __set_page_dirty_nobuffers, |