diff options
Diffstat (limited to 'fs/cifs/file.c')
| -rw-r--r-- | fs/cifs/file.c | 137 | 
1 files changed, 78 insertions, 59 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 81747acca4c..38c06f82657 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -46,7 +46,7 @@ static inline struct cifsFileInfo *cifs_init_private(  	memset(private_data, 0, sizeof(struct cifsFileInfo));  	private_data->netfid = netfid;  	private_data->pid = current->tgid; -	init_MUTEX(&private_data->fh_sem); +	mutex_init(&private_data->fh_mutex);  	mutex_init(&private_data->lock_mutex);  	INIT_LIST_HEAD(&private_data->llist);  	private_data->pfile = file; /* needed for writepage */ @@ -129,15 +129,12 @@ static inline int cifs_posix_open_inode_helper(struct inode *inode,  			struct file *file, struct cifsInodeInfo *pCifsInode,  			struct cifsFileInfo *pCifsFile, int oplock, u16 netfid)  { -	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -/*	struct timespec temp; */   /* BB REMOVEME BB */  	file->private_data = kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);  	if (file->private_data == NULL)  		return -ENOMEM;  	pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);  	write_lock(&GlobalSMBSeslock); -	list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);  	pCifsInode = CIFS_I(file->f_path.dentry->d_inode);  	if (pCifsInode == NULL) { @@ -145,17 +142,6 @@ static inline int cifs_posix_open_inode_helper(struct inode *inode,  		return -EINVAL;  	} -	/* want handles we can use to read with first -	   in the list so we do not have to walk the -	   list to search for one in write_begin */ -	if ((file->f_flags & O_ACCMODE) == O_WRONLY) { -		list_add_tail(&pCifsFile->flist, -			      &pCifsInode->openFileList); -	} else { -		list_add(&pCifsFile->flist, -			 &pCifsInode->openFileList); -	} -  	if (pCifsInode->clientCanCacheRead) {  		/* we have the inode open somewhere else  		   no need to discard cache data */ @@ -284,35 +270,32 @@ int cifs_open(struct inode *inode, struct file *file)  	cifs_sb = CIFS_SB(inode->i_sb);  	tcon = cifs_sb->tcon; -	if (file->f_flags & O_CREAT) { -		/* search inode for this file and fill in file->private_data */ -		pCifsInode = CIFS_I(file->f_path.dentry->d_inode); -		read_lock(&GlobalSMBSeslock); -		list_for_each(tmp, &pCifsInode->openFileList) { -			pCifsFile = list_entry(tmp, struct cifsFileInfo, -					       flist); -			if ((pCifsFile->pfile == NULL) && -			    (pCifsFile->pid == current->tgid)) { -				/* mode set in cifs_create */ +	/* search inode for this file and fill in file->private_data */ +	pCifsInode = CIFS_I(file->f_path.dentry->d_inode); +	read_lock(&GlobalSMBSeslock); +	list_for_each(tmp, &pCifsInode->openFileList) { +		pCifsFile = list_entry(tmp, struct cifsFileInfo, +				       flist); +		if ((pCifsFile->pfile == NULL) && +		    (pCifsFile->pid == current->tgid)) { +			/* mode set in cifs_create */ -				/* needed for writepage */ -				pCifsFile->pfile = file; +			/* needed for writepage */ +			pCifsFile->pfile = file; -				file->private_data = pCifsFile; -				break; -			} -		} -		read_unlock(&GlobalSMBSeslock); -		if (file->private_data != NULL) { -			rc = 0; -			FreeXid(xid); -			return rc; -		} else { -			if (file->f_flags & O_EXCL) -				cERROR(1, ("could not find file instance for " -					   "new file %p", file)); +			file->private_data = pCifsFile; +			break;  		}  	} +	read_unlock(&GlobalSMBSeslock); + +	if (file->private_data != NULL) { +		rc = 0; +		FreeXid(xid); +		return rc; +	} else if ((file->f_flags & O_CREAT) && (file->f_flags & O_EXCL)) +			cERROR(1, ("could not find file instance for " +				   "new file %p", file));  	full_path = build_path_from_dentry(file->f_path.dentry);  	if (full_path == NULL) { @@ -500,9 +483,9 @@ static int cifs_reopen_file(struct file *file, bool can_flush)  		return -EBADF;  	xid = GetXid(); -	down(&pCifsFile->fh_sem); +	mutex_unlock(&pCifsFile->fh_mutex);  	if (!pCifsFile->invalidHandle) { -		up(&pCifsFile->fh_sem); +		mutex_lock(&pCifsFile->fh_mutex);  		FreeXid(xid);  		return 0;  	} @@ -533,7 +516,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)  	if (full_path == NULL) {  		rc = -ENOMEM;  reopen_error_exit: -		up(&pCifsFile->fh_sem); +		mutex_lock(&pCifsFile->fh_mutex);  		FreeXid(xid);  		return rc;  	} @@ -575,14 +558,14 @@ reopen_error_exit:  			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &  				CIFS_MOUNT_MAP_SPECIAL_CHR);  	if (rc) { -		up(&pCifsFile->fh_sem); +		mutex_lock(&pCifsFile->fh_mutex);  		cFYI(1, ("cifs_open returned 0x%x", rc));  		cFYI(1, ("oplock: %d", oplock));  	} else {  reopen_success:  		pCifsFile->netfid = netfid;  		pCifsFile->invalidHandle = false; -		up(&pCifsFile->fh_sem); +		mutex_lock(&pCifsFile->fh_mutex);  		pCifsInode = CIFS_I(inode);  		if (pCifsInode) {  			if (can_flush) { @@ -971,6 +954,40 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  	return rc;  } +/* + * Set the timeout on write requests past EOF. For some servers (Windows) + * these calls can be very long. + * + * If we're writing >10M past the EOF we give a 180s timeout. Anything less + * than that gets a 45s timeout. Writes not past EOF get 15s timeouts. + * The 10M cutoff is totally arbitrary. A better scheme for this would be + * welcome if someone wants to suggest one. + * + * We may be able to do a better job with this if there were some way to + * declare that a file should be sparse. + */ +static int +cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) +{ +	if (offset <= cifsi->server_eof) +		return CIFS_STD_OP; +	else if (offset > (cifsi->server_eof + (10 * 1024 * 1024))) +		return CIFS_VLONG_OP; +	else +		return CIFS_LONG_OP; +} + +/* update the file size (if needed) after a write */ +static void +cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, +		      unsigned int bytes_written) +{ +	loff_t end_of_write = offset + bytes_written; + +	if (end_of_write > cifsi->server_eof) +		cifsi->server_eof = end_of_write; +} +  ssize_t cifs_user_write(struct file *file, const char __user *write_data,  	size_t write_size, loff_t *poffset)  { @@ -981,6 +998,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  	struct cifsTconInfo *pTcon;  	int xid, long_op;  	struct cifsFileInfo *open_file; +	struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode);  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); @@ -1000,11 +1018,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  	xid = GetXid(); -	if (*poffset > file->f_path.dentry->d_inode->i_size) -		long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ -	else -		long_op = CIFS_LONG_OP; - +	long_op = cifs_write_timeout(cifsi, *poffset);  	for (total_written = 0; write_size > total_written;  	     total_written += bytes_written) {  		rc = -EAGAIN; @@ -1048,8 +1062,10 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  				FreeXid(xid);  				return rc;  			} -		} else +		} else { +			cifs_update_eof(cifsi, *poffset, bytes_written);  			*poffset += bytes_written; +		}  		long_op = CIFS_STD_OP; /* subsequent writes fast -  				    15 seconds is plenty */  	} @@ -1085,6 +1101,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,  	struct cifsTconInfo *pTcon;  	int xid, long_op;  	struct cifsFileInfo *open_file; +	struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode);  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); @@ -1099,11 +1116,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,  	xid = GetXid(); -	if (*poffset > file->f_path.dentry->d_inode->i_size) -		long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ -	else -		long_op = CIFS_LONG_OP; - +	long_op = cifs_write_timeout(cifsi, *poffset);  	for (total_written = 0; write_size > total_written;  	     total_written += bytes_written) {  		rc = -EAGAIN; @@ -1166,8 +1179,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data,  				FreeXid(xid);  				return rc;  			} -		} else +		} else { +			cifs_update_eof(cifsi, *poffset, bytes_written);  			*poffset += bytes_written; +		}  		long_op = CIFS_STD_OP; /* subsequent writes fast -  				    15 seconds is plenty */  	} @@ -1380,11 +1395,12 @@ static int cifs_writepages(struct address_space *mapping,  	int nr_pages;  	__u64 offset = 0;  	struct cifsFileInfo *open_file; +	struct cifsInodeInfo *cifsi = CIFS_I(mapping->host);  	struct page *page;  	struct pagevec pvec;  	int rc = 0;  	int scanned = 0; -	int xid; +	int xid, long_op;  	cifs_sb = CIFS_SB(mapping->host->i_sb); @@ -1528,12 +1544,15 @@ retry:  				cERROR(1, ("No writable handles for inode"));  				rc = -EBADF;  			} else { +				long_op = cifs_write_timeout(cifsi, offset);  				rc = CIFSSMBWrite2(xid, cifs_sb->tcon,  						   open_file->netfid,  						   bytes_to_write, offset,  						   &bytes_written, iov, n_iov, -						   CIFS_LONG_OP); +						   long_op);  				atomic_dec(&open_file->wrtPending); +				cifs_update_eof(cifsi, offset, bytes_written); +  				if (rc || bytes_written < bytes_to_write) {  					cERROR(1, ("Write2 ret %d, wrote %d",  						  rc, bytes_written));  |