aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>2012-05-21 10:20:12 -0400
committerPavel Shilovsky <pshilovsky@samba.org>2012-05-23 04:33:18 -0400
commit2c0c2a08bed7a3b791f88d09d16ace56acb3dd98 (patch)
treefe41007499ec9b8371562a4aae78d9a6f6d29c89 /fs/cifs
parentea4b574028f0c30d736ab6b13b518af8533a86c4 (diff)
cifs: fix oops while traversing open file list (try #4)
While traversing the linked list of open file handles, if the identfied file handle is invalid, a reopen is attempted and if it fails, we resume traversing where we stopped and cifs can oops while accessing invalid next element, for list might have changed. So mark the invalid file handle and attempt reopen if no valid file handle is found in rest of the list. If reopen fails, move the invalid file handle to the end of the list and start traversing the list again from the begining. Repeat this four times before giving up and returning an error if file reopen keeps failing. Cc: <stable@vger.kernel.org> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/file.c57
2 files changed, 34 insertions, 24 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c0a027ec63c8..20350a93ed99 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -43,6 +43,7 @@
43 43
44#define CIFS_MIN_RCV_POOL 4 44#define CIFS_MIN_RCV_POOL 4
45 45
46#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */
46/* 47/*
47 * default attribute cache timeout (jiffies) 48 * default attribute cache timeout (jiffies)
48 */ 49 */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index ed9b5a8fb51f..253170dfa716 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -1565,10 +1565,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
1565struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, 1565struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
1566 bool fsuid_only) 1566 bool fsuid_only)
1567{ 1567{
1568 struct cifsFileInfo *open_file; 1568 struct cifsFileInfo *open_file, *inv_file = NULL;
1569 struct cifs_sb_info *cifs_sb; 1569 struct cifs_sb_info *cifs_sb;
1570 bool any_available = false; 1570 bool any_available = false;
1571 int rc; 1571 int rc;
1572 unsigned int refind = 0;
1572 1573
1573 /* Having a null inode here (because mapping->host was set to zero by 1574 /* Having a null inode here (because mapping->host was set to zero by
1574 the VFS or MM) should not happen but we had reports of on oops (due to 1575 the VFS or MM) should not happen but we had reports of on oops (due to
@@ -1588,40 +1589,25 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
1588 1589
1589 spin_lock(&cifs_file_list_lock); 1590 spin_lock(&cifs_file_list_lock);
1590refind_writable: 1591refind_writable:
1592 if (refind > MAX_REOPEN_ATT) {
1593 spin_unlock(&cifs_file_list_lock);
1594 return NULL;
1595 }
1591 list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { 1596 list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
1592 if (!any_available && open_file->pid != current->tgid) 1597 if (!any_available && open_file->pid != current->tgid)
1593 continue; 1598 continue;
1594 if (fsuid_only && open_file->uid != current_fsuid()) 1599 if (fsuid_only && open_file->uid != current_fsuid())
1595 continue; 1600 continue;
1596 if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { 1601 if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
1597 cifsFileInfo_get(open_file);
1598
1599 if (!open_file->invalidHandle) { 1602 if (!open_file->invalidHandle) {
1600 /* found a good writable file */ 1603 /* found a good writable file */
1604 cifsFileInfo_get(open_file);
1601 spin_unlock(&cifs_file_list_lock); 1605 spin_unlock(&cifs_file_list_lock);
1602 return open_file; 1606 return open_file;
1607 } else {
1608 if (!inv_file)
1609 inv_file = open_file;
1603 } 1610 }
1604
1605 spin_unlock(&cifs_file_list_lock);
1606
1607 /* Had to unlock since following call can block */
1608 rc = cifs_reopen_file(open_file, false);
1609 if (!rc)
1610 return open_file;
1611
1612 /* if it fails, try another handle if possible */
1613 cFYI(1, "wp failed on reopen file");
1614 cifsFileInfo_put(open_file);
1615
1616 spin_lock(&cifs_file_list_lock);
1617
1618 /* else we simply continue to the next entry. Thus
1619 we do not loop on reopen errors. If we
1620 can not reopen the file, for example if we
1621 reconnected to a server with another client
1622 racing to delete or lock the file we would not
1623 make progress if we restarted before the beginning
1624 of the loop here. */
1625 } 1611 }
1626 } 1612 }
1627 /* couldn't find useable FH with same pid, try any available */ 1613 /* couldn't find useable FH with same pid, try any available */
@@ -1629,7 +1615,30 @@ refind_writable:
1629 any_available = true; 1615 any_available = true;
1630 goto refind_writable; 1616 goto refind_writable;
1631 } 1617 }
1618
1619 if (inv_file) {
1620 any_available = false;
1621 cifsFileInfo_get(inv_file);
1622 }
1623
1632 spin_unlock(&cifs_file_list_lock); 1624 spin_unlock(&cifs_file_list_lock);
1625
1626 if (inv_file) {
1627 rc = cifs_reopen_file(inv_file, false);
1628 if (!rc)
1629 return inv_file;
1630 else {
1631 spin_lock(&cifs_file_list_lock);
1632 list_move_tail(&inv_file->flist,
1633 &cifs_inode->openFileList);
1634 spin_unlock(&cifs_file_list_lock);
1635 cifsFileInfo_put(inv_file);
1636 spin_lock(&cifs_file_list_lock);
1637 ++refind;
1638 goto refind_writable;
1639 }
1640 }
1641
1633 return NULL; 1642 return NULL;
1634} 1643}
1635 1644