diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/CHANGES | 6 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/file.c | 88 | ||||
-rw-r--r-- | fs/cifs/inode.c | 1 |
5 files changed, 73 insertions, 25 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index f554a70c9cf3..5bab24f59053 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES | |||
@@ -1,3 +1,9 @@ | |||
1 | Version 1.39 | ||
2 | ------------ | ||
3 | Defer close of a file handle slightly if pending writes depend on that file handle | ||
4 | (this reduces the EBADF bad file handle errors that can be logged under heavy | ||
5 | stress on writes). | ||
6 | |||
1 | Version 1.38 | 7 | Version 1.38 |
2 | ------------ | 8 | ------------ |
3 | Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket) | 9 | Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket) |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 4cdb29fdc8c2..1223fa81dbd2 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); | |||
97 | extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); | 97 | extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); |
98 | extern int cifs_ioctl (struct inode * inode, struct file * filep, | 98 | extern int cifs_ioctl (struct inode * inode, struct file * filep, |
99 | unsigned int command, unsigned long arg); | 99 | unsigned int command, unsigned long arg); |
100 | #define CIFS_VERSION "1.38" | 100 | #define CIFS_VERSION "1.39" |
101 | #endif /* _CIFSFS_H */ | 101 | #endif /* _CIFSFS_H */ |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 839a55667c3c..1ba08f8c5bc4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -299,6 +299,7 @@ struct cifsFileInfo { | |||
299 | struct inode * pInode; /* needed for oplock break */ | 299 | struct inode * pInode; /* needed for oplock break */ |
300 | unsigned closePend:1; /* file is marked to close */ | 300 | unsigned closePend:1; /* file is marked to close */ |
301 | unsigned invalidHandle:1; /* file closed via session abend */ | 301 | unsigned invalidHandle:1; /* file closed via session abend */ |
302 | atomic_t wrtPending; /* handle in use - defer close */ | ||
302 | struct semaphore fh_sem; /* prevents reopen race after dead ses*/ | 303 | struct semaphore fh_sem; /* prevents reopen race after dead ses*/ |
303 | char * search_resume_name; /* BB removeme BB */ | 304 | char * search_resume_name; /* BB removeme BB */ |
304 | unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */ | 305 | unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */ |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 23af20d5af7c..da4f5e10b3cc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/pagevec.h> | 29 | #include <linux/pagevec.h> |
30 | #include <linux/smp_lock.h> | 30 | #include <linux/smp_lock.h> |
31 | #include <linux/writeback.h> | 31 | #include <linux/writeback.h> |
32 | #include <linux/delay.h> | ||
32 | #include <asm/div64.h> | 33 | #include <asm/div64.h> |
33 | #include "cifsfs.h" | 34 | #include "cifsfs.h" |
34 | #include "cifspdu.h" | 35 | #include "cifspdu.h" |
@@ -50,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private( | |||
50 | private_data->pInode = inode; | 51 | private_data->pInode = inode; |
51 | private_data->invalidHandle = FALSE; | 52 | private_data->invalidHandle = FALSE; |
52 | private_data->closePend = FALSE; | 53 | private_data->closePend = FALSE; |
54 | /* we have to track num writers to the inode, since writepages | ||
55 | does not tell us which handle the write is for so there can | ||
56 | be a close (overlapping with write) of the filehandle that | ||
57 | cifs_writepages chose to use */ | ||
58 | atomic_set(&private_data->wrtPending,0); | ||
53 | 59 | ||
54 | return private_data; | 60 | return private_data; |
55 | } | 61 | } |
@@ -473,6 +479,20 @@ int cifs_close(struct inode *inode, struct file *file) | |||
473 | /* no sense reconnecting to close a file that is | 479 | /* no sense reconnecting to close a file that is |
474 | already closed */ | 480 | already closed */ |
475 | if (pTcon->tidStatus != CifsNeedReconnect) { | 481 | if (pTcon->tidStatus != CifsNeedReconnect) { |
482 | int timeout = 2; | ||
483 | while((atomic_read(&pSMBFile->wrtPending) != 0) | ||
484 | && (timeout < 1000) ) { | ||
485 | /* Give write a better chance to get to | ||
486 | server ahead of the close. We do not | ||
487 | want to add a wait_q here as it would | ||
488 | increase the memory utilization as | ||
489 | the struct would be in each open file, | ||
490 | but this should give enough time to | ||
491 | clear the socket */ | ||
492 | cERROR(1,("close with pending writes")); | ||
493 | msleep(timeout); | ||
494 | timeout *= 4; | ||
495 | } | ||
476 | write_unlock(&file->f_owner.lock); | 496 | write_unlock(&file->f_owner.lock); |
477 | rc = CIFSSMBClose(xid, pTcon, | 497 | rc = CIFSSMBClose(xid, pTcon, |
478 | pSMBFile->netfid); | 498 | pSMBFile->netfid); |
@@ -919,9 +939,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) | |||
919 | if (open_file->pfile && | 939 | if (open_file->pfile && |
920 | ((open_file->pfile->f_flags & O_RDWR) || | 940 | ((open_file->pfile->f_flags & O_RDWR) || |
921 | (open_file->pfile->f_flags & O_WRONLY))) { | 941 | (open_file->pfile->f_flags & O_WRONLY))) { |
942 | atomic_inc(&open_file->wrtPending); | ||
922 | read_unlock(&GlobalSMBSeslock); | 943 | read_unlock(&GlobalSMBSeslock); |
923 | if((open_file->invalidHandle) && | 944 | if((open_file->invalidHandle) && |
924 | (!open_file->closePend)) { | 945 | (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) { |
925 | rc = cifs_reopen_file(&cifs_inode->vfs_inode, | 946 | rc = cifs_reopen_file(&cifs_inode->vfs_inode, |
926 | open_file->pfile, FALSE); | 947 | open_file->pfile, FALSE); |
927 | /* if it fails, try another handle - might be */ | 948 | /* if it fails, try another handle - might be */ |
@@ -929,6 +950,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) | |||
929 | if(rc) { | 950 | if(rc) { |
930 | cFYI(1,("failed on reopen file in wp")); | 951 | cFYI(1,("failed on reopen file in wp")); |
931 | read_lock(&GlobalSMBSeslock); | 952 | read_lock(&GlobalSMBSeslock); |
953 | /* can not use this handle, no write | ||
954 | pending on this one after all */ | ||
955 | atomic_dec | ||
956 | (&open_file->wrtPending); | ||
932 | continue; | 957 | continue; |
933 | } | 958 | } |
934 | } | 959 | } |
@@ -981,6 +1006,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) | |||
981 | if (open_file) { | 1006 | if (open_file) { |
982 | bytes_written = cifs_write(open_file->pfile, write_data, | 1007 | bytes_written = cifs_write(open_file->pfile, write_data, |
983 | to-from, &offset); | 1008 | to-from, &offset); |
1009 | atomic_dec(&open_file->wrtPending); | ||
984 | /* Does mm or vfs already set times? */ | 1010 | /* Does mm or vfs already set times? */ |
985 | inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); | 1011 | inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); |
986 | if ((bytes_written > 0) && (offset)) { | 1012 | if ((bytes_written > 0) && (offset)) { |
@@ -1016,7 +1042,7 @@ static int cifs_writepages(struct address_space *mapping, | |||
1016 | pgoff_t next; | 1042 | pgoff_t next; |
1017 | int nr_pages; | 1043 | int nr_pages; |
1018 | __u64 offset = 0; | 1044 | __u64 offset = 0; |
1019 | struct cifsFileInfo *open_file = NULL; | 1045 | struct cifsFileInfo *open_file; |
1020 | struct page *page; | 1046 | struct page *page; |
1021 | struct pagevec pvec; | 1047 | struct pagevec pvec; |
1022 | int rc = 0; | 1048 | int rc = 0; |
@@ -1071,15 +1097,6 @@ retry: | |||
1071 | int first; | 1097 | int first; |
1072 | unsigned int i; | 1098 | unsigned int i; |
1073 | 1099 | ||
1074 | if (!open_file) { | ||
1075 | open_file = find_writable_file(CIFS_I(mapping->host)); | ||
1076 | if (!open_file) { | ||
1077 | pagevec_release(&pvec); | ||
1078 | cERROR(1, ("No writable handles for inode")); | ||
1079 | return -EIO; | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | first = -1; | 1100 | first = -1; |
1084 | next = 0; | 1101 | next = 0; |
1085 | n_iov = 0; | 1102 | n_iov = 0; |
@@ -1155,18 +1172,32 @@ retry: | |||
1155 | break; | 1172 | break; |
1156 | } | 1173 | } |
1157 | if (n_iov) { | 1174 | if (n_iov) { |
1158 | rc = CIFSSMBWrite2(xid, cifs_sb->tcon, | 1175 | /* Search for a writable handle every time we call |
1159 | open_file->netfid, bytes_to_write, | 1176 | * CIFSSMBWrite2. We can't rely on the last handle |
1160 | offset, &bytes_written, iov, n_iov, | 1177 | * we used to still be valid |
1161 | 1); | 1178 | */ |
1162 | if (rc || bytes_written < bytes_to_write) { | 1179 | open_file = find_writable_file(CIFS_I(mapping->host)); |
1163 | cERROR(1,("CIFSSMBWrite2 returned %d, written = %x", | 1180 | if (!open_file) { |
1164 | rc, bytes_written)); | 1181 | cERROR(1, ("No writable handles for inode")); |
1165 | set_bit(AS_EIO, &mapping->flags); | 1182 | rc = -EBADF; |
1166 | SetPageError(page); | ||
1167 | } else { | 1183 | } else { |
1168 | cifs_stats_bytes_written(cifs_sb->tcon, | 1184 | rc = CIFSSMBWrite2(xid, cifs_sb->tcon, |
1169 | bytes_written); | 1185 | open_file->netfid, |
1186 | bytes_to_write, offset, | ||
1187 | &bytes_written, iov, n_iov, | ||
1188 | 1); | ||
1189 | atomic_dec(&open_file->wrtPending); | ||
1190 | if (rc || bytes_written < bytes_to_write) { | ||
1191 | cERROR(1,("Write2 ret %d, written = %d", | ||
1192 | rc, bytes_written)); | ||
1193 | /* BB what if continued retry is | ||
1194 | requested via mount flags? */ | ||
1195 | set_bit(AS_EIO, &mapping->flags); | ||
1196 | SetPageError(page); | ||
1197 | } else { | ||
1198 | cifs_stats_bytes_written(cifs_sb->tcon, | ||
1199 | bytes_written); | ||
1200 | } | ||
1170 | } | 1201 | } |
1171 | for (i = 0; i < n_iov; i++) { | 1202 | for (i = 0; i < n_iov; i++) { |
1172 | page = pvec.pages[first + i]; | 1203 | page = pvec.pages[first + i]; |
@@ -1788,9 +1819,18 @@ static int cifs_readpage(struct file *file, struct page *page) | |||
1788 | page caching in the current Linux kernel design */ | 1819 | page caching in the current Linux kernel design */ |
1789 | int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) | 1820 | int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) |
1790 | { | 1821 | { |
1791 | if (cifsInode && find_writable_file(cifsInode)) | 1822 | struct cifsFileInfo *open_file = NULL; |
1823 | |||
1824 | if (cifsInode) | ||
1825 | open_file = find_writable_file(cifsInode); | ||
1826 | |||
1827 | if(open_file) { | ||
1828 | /* there is not actually a write pending so let | ||
1829 | this handle go free and allow it to | ||
1830 | be closable if needed */ | ||
1831 | atomic_dec(&open_file->wrtPending); | ||
1792 | return 0; | 1832 | return 0; |
1793 | else | 1833 | } else |
1794 | return 1; | 1834 | return 1; |
1795 | } | 1835 | } |
1796 | 1836 | ||
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ff4d1cc7c248..912d401600f6 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -1006,6 +1006,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1006 | __u32 npid = open_file->pid; | 1006 | __u32 npid = open_file->pid; |
1007 | rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, | 1007 | rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, |
1008 | nfid, npid, FALSE); | 1008 | nfid, npid, FALSE); |
1009 | atomic_dec(&open_file->wrtPending); | ||
1009 | cFYI(1,("SetFSize for attrs rc = %d", rc)); | 1010 | cFYI(1,("SetFSize for attrs rc = %d", rc)); |
1010 | if(rc == -EINVAL) { | 1011 | if(rc == -EINVAL) { |
1011 | int bytes_written; | 1012 | int bytes_written; |