X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=fs%2Fcifs%2Ffile.c;h=14a1c72ced92e1cc98024494ea217b2a7413921d;hb=52fd3d6fea441835fe3a35b7280e5e128bdeca9b;hp=11806c879c4715e21cee08a102403cb191c633da;hpb=0ae0efada36219024e4e3008f16c993d5d091280;p=linux-2.6-omap-h63xx.git diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 11806c879c4..14a1c72ced9 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" @@ -50,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private( private_data->pInode = inode; private_data->invalidHandle = FALSE; private_data->closePend = FALSE; + /* we have to track num writers to the inode, since writepages + does not tell us which handle the write is for so there can + be a close (overlapping with write) of the filehandle that + cifs_writepages chose to use */ + atomic_set(&private_data->wrtPending,0); return private_data; } @@ -473,6 +479,22 @@ int cifs_close(struct inode *inode, struct file *file) /* no sense reconnecting to close a file that is already closed */ if (pTcon->tidStatus != CifsNeedReconnect) { + int timeout = 2; + while((atomic_read(&pSMBFile->wrtPending) != 0) + && (timeout < 1000) ) { + /* Give write a better chance to get to + server ahead of the close. We do not + want to add a wait_q here as it would + increase the memory utilization as + the struct would be in each open file, + but this should give enough time to + clear the socket */ + write_unlock(&file->f_owner.lock); + cERROR(1,("close with pending writes")); + msleep(timeout); + write_lock(&file->f_owner.lock); + timeout *= 4; + } write_unlock(&file->f_owner.lock); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); @@ -919,9 +941,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { + atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); if((open_file->invalidHandle) && - (!open_file->closePend)) { + (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) { rc = cifs_reopen_file(&cifs_inode->vfs_inode, open_file->pfile, FALSE); /* if it fails, try another handle - might be */ @@ -929,6 +952,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) if(rc) { cFYI(1,("failed on reopen file in wp")); read_lock(&GlobalSMBSeslock); + /* can not use this handle, no write + pending on this one after all */ + atomic_dec + (&open_file->wrtPending); continue; } } @@ -981,6 +1008,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) if (open_file) { bytes_written = cifs_write(open_file->pfile, write_data, to-from, &offset); + atomic_dec(&open_file->wrtPending); /* Does mm or vfs already set times? */ inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); if ((bytes_written > 0) && (offset)) { @@ -1011,11 +1039,12 @@ static int cifs_writepages(struct address_space *mapping, pgoff_t index; int is_range = 0; struct kvec iov[32]; + int len; int n_iov = 0; pgoff_t next; int nr_pages; __u64 offset = 0; - struct cifsFileInfo *open_file = NULL; + struct cifsFileInfo *open_file; struct page *page; struct pagevec pvec; int rc = 0; @@ -1070,15 +1099,6 @@ retry: int first; unsigned int i; - if (!open_file) { - open_file = find_writable_file(CIFS_I(mapping->host)); - if (!open_file) { - pagevec_release(&pvec); - cERROR(1, ("No writable handles for inode")); - return -EIO; - } - } - first = -1; next = 0; n_iov = 0; @@ -1124,16 +1144,26 @@ retry: unlock_page(page); break; } + + if (page_offset(page) >= mapping->host->i_size) { + done = 1; + unlock_page(page); + break; + } + /* * BB can we get rid of this? pages are held by pvec */ page_cache_get(page); + len = min(mapping->host->i_size - page_offset(page), + (loff_t)PAGE_CACHE_SIZE); + /* reserve iov[0] for the smb header */ n_iov++; iov[n_iov].iov_base = kmap(page); - iov[n_iov].iov_len = PAGE_CACHE_SIZE; - bytes_to_write += PAGE_CACHE_SIZE; + iov[n_iov].iov_len = len; + bytes_to_write += len; if (first < 0) { first = i; @@ -1144,15 +1174,32 @@ retry: break; } if (n_iov) { - rc = CIFSSMBWrite2(xid, cifs_sb->tcon, - open_file->netfid, bytes_to_write, - offset, &bytes_written, iov, n_iov, - 1); - if (rc || bytes_written < bytes_to_write) { - cERROR(1,("CIFSSMBWrite2 returned %d, written = %x", - rc, bytes_written)); - set_bit(AS_EIO, &mapping->flags); - SetPageError(page); + /* Search for a writable handle every time we call + * CIFSSMBWrite2. We can't rely on the last handle + * we used to still be valid + */ + open_file = find_writable_file(CIFS_I(mapping->host)); + if (!open_file) { + cERROR(1, ("No writable handles for inode")); + rc = -EBADF; + } else { + rc = CIFSSMBWrite2(xid, cifs_sb->tcon, + open_file->netfid, + bytes_to_write, offset, + &bytes_written, iov, n_iov, + 1); + atomic_dec(&open_file->wrtPending); + if (rc || bytes_written < bytes_to_write) { + cERROR(1,("Write2 ret %d, written = %d", + rc, bytes_written)); + /* BB what if continued retry is + requested via mount flags? */ + set_bit(AS_EIO, &mapping->flags); + SetPageError(page); + } else { + cifs_stats_bytes_written(cifs_sb->tcon, + bytes_written); + } } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; @@ -1774,9 +1821,18 @@ static int cifs_readpage(struct file *file, struct page *page) page caching in the current Linux kernel design */ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) { - if (cifsInode && find_writable_file(cifsInode)) + struct cifsFileInfo *open_file = NULL; + + if (cifsInode) + open_file = find_writable_file(cifsInode); + + if(open_file) { + /* there is not actually a write pending so let + this handle go free and allow it to + be closable if needed */ + atomic_dec(&open_file->wrtPending); return 0; - else + } else return 1; }