Commit 629d5ab8 authored by Steve French's avatar Steve French Committed by Steve French

Fix oops caused by lack of spinlock protection on some lists. Fix display

of NTFS dfs junctions (which now correctly appear as symlinks).
Return writebehind caching errors on file close.
parent 4708c3cd
...@@ -20,3 +20,9 @@ Patch Contributors ...@@ -20,3 +20,9 @@ Patch Contributors
Zwane Mwaikambo Zwane Mwaikambo
Andi Kleen Andi Kleen
Test case and Bug Report contributors
-------------------------------------
Thanks to those in the community who have submitted detailed bug reports
and debug of problems they have found: Jochen Dolze, David Blaine,
Rene Scharfe, Martin Josefsson and others.
Version 0.77
------------
Fix display of NTFS DFS junctions to display as symlinks.
They are the network equivalent. Fix oops in
cifs_partialpagewrite caused by missing spinlock protection
of openfile linked list. Allow writebehind caching errors to
be returned to the application at file close.
Version 0.76 Version 0.76
------------ ------------
Clean up options displayed in /proc/mounts by show_options to Clean up options displayed in /proc/mounts by show_options to
......
...@@ -18,6 +18,8 @@ c) multi-user mounts - multiplexed sessionsetups over single vc ...@@ -18,6 +18,8 @@ c) multi-user mounts - multiplexed sessionsetups over single vc
d) Kerberos/SPNEGO session setup support - (started) d) Kerberos/SPNEGO session setup support - (started)
e) NTLMv2 authentication and MD5-HMAC signing SMB PDUs - (mostly implemented) e) NTLMv2 authentication and MD5-HMAC signing SMB PDUs - (mostly implemented)
signing necessary for some Windows 2003 servers in domain
controller mode.
f) oplock support (ie safe CIFS distributed file caching) is not quite complete. f) oplock support (ie safe CIFS distributed file caching) is not quite complete.
In addition Directory entry caching relies on a 1 second timer, rather than In addition Directory entry caching relies on a 1 second timer, rather than
...@@ -52,7 +54,8 @@ file opens on top of each other by incrementing reference count rather ...@@ -52,7 +54,8 @@ file opens on top of each other by incrementing reference count rather
than resending (helps reduce server resource utilization and avoid than resending (helps reduce server resource utilization and avoid
spurious oplock breaks). spurious oplock breaks).
KNOWN BUGS (updated March 7, 2003)
KNOWN BUGS (updated May 16, 2003)
==================================== ====================================
1) existing symbolic links (Windows reparse points) are recognized but 1) existing symbolic links (Windows reparse points) are recognized but
can not be created remotely. They are implemented for Samba and those that can not be created remotely. They are implemented for Samba and those that
...@@ -60,6 +63,11 @@ support the CIFS Unix extensions but Samba has a bug currently handling ...@@ -60,6 +63,11 @@ support the CIFS Unix extensions but Samba has a bug currently handling
symlink text beginning with slash symlink text beginning with slash
2) delete of file with read-only attribute set will fail (may be ok) 2) delete of file with read-only attribute set will fail (may be ok)
3) mount helper syntax not quite matching man page 3) mount helper syntax not quite matching man page
4) follow_link and readdir code does not follow dfs junctions
but recognizes them
5) create of new files to FAT partitions on Windows servers can
succeed but still return access denied (appears to be Windows
not client problem). NTFS partitions do not have this problem.
Misc testing to do Misc testing to do
================= =================
...@@ -68,6 +76,6 @@ types. ...@@ -68,6 +76,6 @@ types.
2) Run dbench 2) Run dbench
3) Finish FSX testing on SMP now that we workaround the Samba bug 3) Finish high stress fsx testing on SMP clients
4) Additional performance testing and optimization 4) Additional performance testing and optimization
...@@ -427,6 +427,7 @@ cifs_destroy_mids(void) ...@@ -427,6 +427,7 @@ cifs_destroy_mids(void)
static int cifs_oplock_thread(void * dummyarg) static int cifs_oplock_thread(void * dummyarg)
{ {
struct list_head * tmp; struct list_head * tmp;
struct list_head * tmp1;
struct oplock_q_entry * oplock_item; struct oplock_q_entry * oplock_item;
struct file * pfile; struct file * pfile;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
...@@ -442,9 +443,8 @@ static int cifs_oplock_thread(void * dummyarg) ...@@ -442,9 +443,8 @@ static int cifs_oplock_thread(void * dummyarg)
/* BB add missing code */ /* BB add missing code */
cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */ cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */
write_lock(&GlobalMid_Lock); write_lock(&GlobalMid_Lock);
list_for_each(tmp, &GlobalOplock_Q) { list_for_each_safe(tmp, tmp1, &GlobalOplock_Q) {
oplock_item = list_entry(tmp, struct oplock_item = list_entry(tmp, struct oplock_q_entry,
oplock_q_entry,
qhead); qhead);
if(oplock_item) { if(oplock_item) {
pTcon = oplock_item->tcon; pTcon = oplock_item->tcon;
...@@ -453,6 +453,9 @@ static int cifs_oplock_thread(void * dummyarg) ...@@ -453,6 +453,9 @@ static int cifs_oplock_thread(void * dummyarg)
DeleteOplockQEntry(oplock_item); DeleteOplockQEntry(oplock_item);
write_unlock(&GlobalMid_Lock); write_unlock(&GlobalMid_Lock);
rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping); rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping);
if(rc)
CIFS_I(pfile->f_dentry->d_inode)->write_behind_rc
= rc;
cFYI(1,("Oplock flush file %p rc %d",pfile,rc)); cFYI(1,("Oplock flush file %p rc %d",pfile,rc));
/* send oplock break */ /* send oplock break */
write_lock(&GlobalMid_Lock); write_lock(&GlobalMid_Lock);
......
...@@ -221,6 +221,7 @@ struct cifsInodeInfo { ...@@ -221,6 +221,7 @@ struct cifsInodeInfo {
struct list_head lockList; struct list_head lockList;
/* BB add in lists for dirty pages - i.e. write caching info for oplock */ /* BB add in lists for dirty pages - i.e. write caching info for oplock */
struct list_head openFileList; struct list_head openFileList;
int write_behind_rc;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
atomic_t inUse; /* num concurrent users (local openers cifs) of file*/ atomic_t inUse; /* num concurrent users (local openers cifs) of file*/
unsigned long time; /* jiffies of last update/check of inode */ unsigned long time; /* jiffies of last update/check of inode */
......
...@@ -110,10 +110,14 @@ cifs_open(struct inode *inode, struct file *file) ...@@ -110,10 +110,14 @@ cifs_open(struct inode *inode, struct file *file)
pCifsFile->netfid = netfid; pCifsFile->netfid = netfid;
pCifsFile->pid = current->pid; pCifsFile->pid = current->pid;
pCifsFile->pfile = file; /* needed for writepage */ pCifsFile->pfile = file; /* needed for writepage */
write_lock(&file->f_owner.lock);
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist,&pTcon->openFileList); list_add(&pCifsFile->tlist,&pTcon->openFileList);
pCifsInode = CIFS_I(file->f_dentry->d_inode); pCifsInode = CIFS_I(file->f_dentry->d_inode);
if(pCifsInode->openFileList.next) if(pCifsInode->openFileList.next)
list_add(&pCifsFile->flist,&pCifsInode->openFileList); list_add(&pCifsFile->flist,&pCifsInode->openFileList);
write_unlock(&GlobalSMBSeslock);
write_unlock(&file->f_owner.lock);
if(file->f_flags & O_CREAT) { if(file->f_flags & O_CREAT) {
/* time to set mode which we can not set earlier due /* time to set mode which we can not set earlier due
to problems creating new read-only files */ to problems creating new read-only files */
...@@ -154,9 +158,11 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo) ...@@ -154,9 +158,11 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
struct cifsFileInfo *open_file = NULL; struct cifsFileInfo *open_file = NULL;
struct file * file = NULL; struct file * file = NULL;
struct list_head *tmp; struct list_head *tmp;
struct list_head *tmp1;
/* list all files open on tree connection */ /* list all files open on tree connection */
list_for_each(tmp, &pTcon->openFileList) { read_lock(&GlobalSMBSeslock);
list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist); open_file = list_entry(tmp,struct cifsFileInfo, flist);
if(open_file) { if(open_file) {
if(open_file->search_resume_name) { if(open_file->search_resume_name) {
...@@ -166,7 +172,9 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo) ...@@ -166,7 +172,9 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
kfree(open_file); kfree(open_file);
if(file) { if(file) {
file->private_data = NULL; file->private_data = NULL;
read_unlock(&GlobalSMBSeslock);
rc = cifs_open(file->f_dentry->d_inode,file); rc = cifs_open(file->f_dentry->d_inode,file);
read_lock(&GlobalSMBSeslock);
if(rc) { if(rc) {
cFYI(1,("reconnecting file %s failed with %d", cFYI(1,("reconnecting file %s failed with %d",
file->f_dentry->d_name.name,rc)); file->f_dentry->d_name.name,rc));
...@@ -177,6 +185,7 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo) ...@@ -177,6 +185,7 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
} }
} }
} }
read_unlock(&GlobalSMBSeslock);
return rc; return rc;
} }
...@@ -195,9 +204,11 @@ cifs_close(struct inode *inode, struct file *file) ...@@ -195,9 +204,11 @@ cifs_close(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon; pTcon = cifs_sb->tcon;
if (pSMBFile) { if (pSMBFile) {
write_lock(&file->f_owner.lock);
if(pSMBFile->flist.next) if(pSMBFile->flist.next)
list_del(&pSMBFile->flist); list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist); list_del(&pSMBFile->tlist);
write_unlock(&file->f_owner.lock);
rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid);
if(pSMBFile->search_resume_name) if(pSMBFile->search_resume_name)
kfree(pSMBFile->search_resume_name); kfree(pSMBFile->search_resume_name);
...@@ -206,6 +217,8 @@ cifs_close(struct inode *inode, struct file *file) ...@@ -206,6 +217,8 @@ cifs_close(struct inode *inode, struct file *file)
} else } else
rc = -EBADF; rc = -EBADF;
if((rc ==0) && CIFS_I(inode)->write_behind_rc)
rc = CIFS_I(inode)->write_behind_rc;
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
...@@ -407,6 +420,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) ...@@ -407,6 +420,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
struct cifsInodeInfo *cifsInode; struct cifsInodeInfo *cifsInode;
struct cifsFileInfo *open_file = NULL; struct cifsFileInfo *open_file = NULL;
struct list_head *tmp; struct list_head *tmp;
struct list_head *tmp1;
int xid; int xid;
xid = GetXid(); xid = GetXid();
...@@ -445,14 +459,17 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) ...@@ -445,14 +459,17 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
cifsInode = CIFS_I(mapping->host); cifsInode = CIFS_I(mapping->host);
list_for_each(tmp, &cifsInode->openFileList) { read_lock(&GlobalSMBSeslock);
list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist); open_file = list_entry(tmp,struct cifsFileInfo, flist);
/* We could check if file is open for writing first */ /* We could check if file is open for writing first */
if((open_file->pfile) && if((open_file->pfile) &&
((open_file->pfile->f_flags & O_RDWR) || ((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) { (open_file->pfile->f_flags & O_WRONLY))) {
read_unlock(&GlobalSMBSeslock);
bytes_written = cifs_write(open_file->pfile, write_data, bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset); to-from, &offset);
read_lock(&GlobalSMBSeslock);
/* Does mm or vfs already set times? */ /* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = CURRENT_TIME; inode->i_atime = inode->i_mtime = CURRENT_TIME;
if ((bytes_written > 0) && (offset)) { if ((bytes_written > 0) && (offset)) {
...@@ -462,6 +479,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) ...@@ -462,6 +479,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
} }
} }
} }
read_unlock(&GlobalSMBSeslock);
if(open_file == NULL) { if(open_file == NULL) {
cFYI(1,("No writeable filehandles for inode")); cFYI(1,("No writeable filehandles for inode"));
rc = -EIO; rc = -EIO;
...@@ -548,7 +566,8 @@ cifs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -548,7 +566,8 @@ cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
dentry->d_name.name, datasync)); dentry->d_name.name, datasync));
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
if(rc == 0)
CIFS_I(inode)->write_behind_rc = 0;
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
...@@ -600,6 +619,9 @@ int cifs_flush(struct file *file) ...@@ -600,6 +619,9 @@ int cifs_flush(struct file *file)
/* filemapfdatawrite appears easier for the time being */ /* filemapfdatawrite appears easier for the time being */
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
if(rc == 0) /* reset wb rc if we were able to write out dirty pages */
CIFS_I(inode)->write_behind_rc = 0;
cFYI(1,("Flush inode %p file %p rc %d",inode,file,rc)); cFYI(1,("Flush inode %p file %p rc %d",inode,file,rc));
return rc; return rc;
...@@ -678,9 +700,13 @@ static void cifs_copy_cache_pages(struct address_space *mapping, ...@@ -678,9 +700,13 @@ static void cifs_copy_cache_pages(struct address_space *mapping,
while (bytes_read > 0) { while (bytes_read > 0) {
if(list_empty(pages)) if(list_empty(pages))
break; break;
spin_lock(&mapping->page_lock);
page = list_entry(pages->prev, struct page, list); page = list_entry(pages->prev, struct page, list);
list_del(&page->list); list_del(&page->list);
spin_unlock(&mapping->page_lock);
if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) {
page_cache_release(page); page_cache_release(page);
cFYI(1,("Add page cache failed")); cFYI(1,("Add page cache failed"));
...@@ -739,12 +765,14 @@ cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -739,12 +765,14 @@ cifs_readpages(struct file *file, struct address_space *mapping,
pagevec_init(&lru_pvec, 0); pagevec_init(&lru_pvec, 0);
for(i = 0;i<num_pages;) { for(i = 0;i<num_pages;) {
spin_lock(&mapping->page_lock);
if(list_empty(page_list)) { if(list_empty(page_list)) {
break; break;
} }
page = list_entry(page_list->prev, struct page, list); page = list_entry(page_list->prev, struct page, list);
offset = (loff_t)page->index << PAGE_CACHE_SHIFT; offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
spin_unlock(&mapping->page_lock);
/* for reads over a certain size could initiate async read ahead */ /* for reads over a certain size could initiate async read ahead */
cFYI(0,("Read %d pages into cache at offset %ld ", cFYI(0,("Read %d pages into cache at offset %ld ",
...@@ -762,12 +790,15 @@ cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -762,12 +790,15 @@ cifs_readpages(struct file *file, struct address_space *mapping,
if ((rc < 0) || (smb_read_data == NULL)) { if ((rc < 0) || (smb_read_data == NULL)) {
cFYI(1,("Read error in readpages: %d",rc)); cFYI(1,("Read error in readpages: %d",rc));
/* clean up remaing pages off list */ /* clean up remaing pages off list */
spin_lock(&mapping->page_lock);
while (!list_empty(page_list) && (i < num_pages)) { while (!list_empty(page_list) && (i < num_pages)) {
page = list_entry(page_list->prev, struct page, list); page = list_entry(page_list->prev, struct page, list);
list_del(&page->list); list_del(&page->list);
} }
spin_unlock(&mapping->page_lock);
break; break;
} else if (bytes_read > 0){ } else if (bytes_read > 0) {
pSMBr = (struct smb_com_read_rsp *)smb_read_data; pSMBr = (struct smb_com_read_rsp *)smb_read_data;
cifs_copy_cache_pages(mapping, page_list, bytes_read, cifs_copy_cache_pages(mapping, page_list, bytes_read,
smb_read_data + 4 /* RFC1000 hdr */ + smb_read_data + 4 /* RFC1000 hdr */ +
......
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