diff options
Diffstat (limited to 'fs/cifs/file.c')
| -rw-r--r-- | fs/cifs/file.c | 170 | 
1 files changed, 135 insertions, 35 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index fa7b0fa72bb3..043288b5c728 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -288,6 +288,8 @@ cifs_down_write(struct rw_semaphore *sem)  		msleep(10);  } +static void cifsFileInfo_put_work(struct work_struct *work); +  struct cifsFileInfo *  cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  		  struct tcon_link *tlink, __u32 oplock) @@ -313,9 +315,6 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  	INIT_LIST_HEAD(&fdlocks->locks);  	fdlocks->cfile = cfile;  	cfile->llist = fdlocks; -	cifs_down_write(&cinode->lock_sem); -	list_add(&fdlocks->llist, &cinode->llist); -	up_write(&cinode->lock_sem);  	cfile->count = 1;  	cfile->pid = current->tgid; @@ -325,6 +324,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  	cfile->invalidHandle = false;  	cfile->tlink = cifs_get_tlink(tlink);  	INIT_WORK(&cfile->oplock_break, cifs_oplock_break); +	INIT_WORK(&cfile->put, cifsFileInfo_put_work);  	mutex_init(&cfile->fh_mutex);  	spin_lock_init(&cfile->file_info_lock); @@ -339,6 +339,10 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  		oplock = 0;  	} +	cifs_down_write(&cinode->lock_sem); +	list_add(&fdlocks->llist, &cinode->llist); +	up_write(&cinode->lock_sem); +  	spin_lock(&tcon->open_file_lock);  	if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)  		oplock = fid->pending_open->oplock; @@ -375,6 +379,41 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)  	return cifs_file;  } +static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) +{ +	struct inode *inode = d_inode(cifs_file->dentry); +	struct cifsInodeInfo *cifsi = CIFS_I(inode); +	struct cifsLockInfo *li, *tmp; +	struct super_block *sb = inode->i_sb; + +	/* +	 * Delete any outstanding lock records. We'll lose them when the file +	 * is closed anyway. +	 */ +	cifs_down_write(&cifsi->lock_sem); +	list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { +		list_del(&li->llist); +		cifs_del_lock_waiters(li); +		kfree(li); +	} +	list_del(&cifs_file->llist->llist); +	kfree(cifs_file->llist); +	up_write(&cifsi->lock_sem); + +	cifs_put_tlink(cifs_file->tlink); +	dput(cifs_file->dentry); +	cifs_sb_deactive(sb); +	kfree(cifs_file); +} + +static void cifsFileInfo_put_work(struct work_struct *work) +{ +	struct cifsFileInfo *cifs_file = container_of(work, +			struct cifsFileInfo, put); + +	cifsFileInfo_put_final(cifs_file); +} +  /**   * cifsFileInfo_put - release a reference of file priv data   * @@ -382,15 +421,15 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)   */  void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  { -	_cifsFileInfo_put(cifs_file, true); +	_cifsFileInfo_put(cifs_file, true, true);  }  /**   * _cifsFileInfo_put - release a reference of file priv data   *   * This may involve closing the filehandle @cifs_file out on the - * server. Must be called without holding tcon->open_file_lock and - * cifs_file->file_info_lock. + * server. Must be called without holding tcon->open_file_lock, + * cinode->open_file_lock and cifs_file->file_info_lock.   *   * If @wait_for_oplock_handler is true and we are releasing the last   * reference, wait for any running oplock break handler of the file @@ -398,7 +437,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)   * oplock break handler, you need to pass false.   *   */ -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, +		       bool wait_oplock_handler, bool offload)  {  	struct inode *inode = d_inode(cifs_file->dentry);  	struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); @@ -406,7 +446,6 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)  	struct cifsInodeInfo *cifsi = CIFS_I(inode);  	struct super_block *sb = inode->i_sb;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); -	struct cifsLockInfo *li, *tmp;  	struct cifs_fid fid;  	struct cifs_pending_open open;  	bool oplock_break_cancelled; @@ -457,7 +496,9 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)  		unsigned int xid;  		xid = get_xid(); -		if (server->ops->close) +		if (server->ops->close_getattr) +			server->ops->close_getattr(xid, tcon, cifs_file); +		else if (server->ops->close)  			server->ops->close(xid, tcon, &cifs_file->fid);  		_free_xid(xid);  	} @@ -467,24 +508,10 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)  	cifs_del_pending_open(&open); -	/* -	 * Delete any outstanding lock records. We'll lose them when the file -	 * is closed anyway. -	 */ -	cifs_down_write(&cifsi->lock_sem); -	list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { -		list_del(&li->llist); -		cifs_del_lock_waiters(li); -		kfree(li); -	} -	list_del(&cifs_file->llist->llist); -	kfree(cifs_file->llist); -	up_write(&cifsi->lock_sem); - -	cifs_put_tlink(cifs_file->tlink); -	dput(cifs_file->dentry); -	cifs_sb_deactive(sb); -	kfree(cifs_file); +	if (offload) +		queue_work(fileinfo_put_wq, &cifs_file->put); +	else +		cifsFileInfo_put_final(cifs_file);  }  int cifs_open(struct inode *inode, struct file *file) @@ -728,6 +755,13 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)  	if (backup_cred(cifs_sb))  		create_options |= CREATE_OPEN_BACKUP_INTENT; +	/* O_SYNC also has bit for O_DSYNC so following check picks up either */ +	if (cfile->f_flags & O_SYNC) +		create_options |= CREATE_WRITE_THROUGH; + +	if (cfile->f_flags & O_DIRECT) +		create_options |= CREATE_NO_BUFFER; +  	if (server->ops->get_lease_key)  		server->ops->get_lease_key(inode, &cfile->fid); @@ -808,7 +842,7 @@ reopen_error_exit:  int cifs_close(struct inode *inode, struct file *file)  {  	if (file->private_data != NULL) { -		cifsFileInfo_put(file->private_data); +		_cifsFileInfo_put(file->private_data, true, false);  		file->private_data = NULL;  	} @@ -1681,7 +1715,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,  		rc = server->ops->mand_unlock_range(cfile, flock, xid);  out: -	if (flock->fl_flags & FL_POSIX) { +	if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) {  		/*  		 * If this is a request to remove all locks because we  		 * are closing the file, it doesn't matter if the @@ -1698,6 +1732,52 @@ out:  	return rc;  } +int cifs_flock(struct file *file, int cmd, struct file_lock *fl) +{ +	int rc, xid; +	int lock = 0, unlock = 0; +	bool wait_flag = false; +	bool posix_lck = false; +	struct cifs_sb_info *cifs_sb; +	struct cifs_tcon *tcon; +	struct cifsFileInfo *cfile; +	__u32 type; + +	rc = -EACCES; +	xid = get_xid(); + +	if (!(fl->fl_flags & FL_FLOCK)) +		return -ENOLCK; + +	cfile = (struct cifsFileInfo *)file->private_data; +	tcon = tlink_tcon(cfile->tlink); + +	cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag, +			tcon->ses->server); +	cifs_sb = CIFS_FILE_SB(file); + +	if (cap_unix(tcon->ses) && +	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && +	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) +		posix_lck = true; + +	if (!lock && !unlock) { +		/* +		 * if no lock or unlock then nothing to do since we do not +		 * know what it is +		 */ +		free_xid(xid); +		return -EOPNOTSUPP; +	} + +	rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock, +			xid); +	free_xid(xid); +	return rc; + + +} +  int cifs_lock(struct file *file, int cmd, struct file_lock *flock)  {  	int rc, xid; @@ -2757,9 +2837,17 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,  		if (!rc) {  			if (wdata->cfile->invalidHandle)  				rc = -EAGAIN; -			else +			else { +#ifdef CONFIG_CIFS_SMB_DIRECT +				if (wdata->mr) { +					wdata->mr->need_invalidate = true; +					smbd_deregister_mr(wdata->mr); +					wdata->mr = NULL; +				} +#endif  				rc = server->ops->async_writev(wdata,  					cifs_uncached_writedata_release); +			}  		}  		/* If the write was successfully sent, we are done */ @@ -3482,8 +3570,16 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,  		if (!rc) {  			if (rdata->cfile->invalidHandle)  				rc = -EAGAIN; -			else +			else { +#ifdef CONFIG_CIFS_SMB_DIRECT +				if (rdata->mr) { +					rdata->mr->need_invalidate = true; +					smbd_deregister_mr(rdata->mr); +					rdata->mr = NULL; +				} +#endif  				rc = server->ops->async_readv(rdata); +			}  		}  		/* If the read was successfully sent, we are done */ @@ -4637,12 +4733,13 @@ void cifs_oplock_break(struct work_struct *work)  	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);  	struct TCP_Server_Info *server = tcon->ses->server;  	int rc = 0; +	bool purge_cache = false;  	wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,  			TASK_UNINTERRUPTIBLE); -	server->ops->downgrade_oplock(server, cinode, -		test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags)); +	server->ops->downgrade_oplock(server, cinode, cfile->oplock_level, +				      cfile->oplock_epoch, &purge_cache);  	if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&  						cifs_has_mand_locks(cinode)) { @@ -4657,18 +4754,21 @@ void cifs_oplock_break(struct work_struct *work)  		else  			break_lease(inode, O_WRONLY);  		rc = filemap_fdatawrite(inode->i_mapping); -		if (!CIFS_CACHE_READ(cinode)) { +		if (!CIFS_CACHE_READ(cinode) || purge_cache) {  			rc = filemap_fdatawait(inode->i_mapping);  			mapping_set_error(inode->i_mapping, rc);  			cifs_zap_mapping(inode);  		}  		cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); +		if (CIFS_CACHE_WRITE(cinode)) +			goto oplock_break_ack;  	}  	rc = cifs_push_locks(cfile);  	if (rc)  		cifs_dbg(VFS, "Push locks rc = %d\n", rc); +oplock_break_ack:  	/*  	 * releasing stale oplock after recent reconnect of smb session using  	 * a now incorrect file handle is not a data integrity issue but do @@ -4680,7 +4780,7 @@ void cifs_oplock_break(struct work_struct *work)  							     cinode);  		cifs_dbg(FYI, "Oplock release rc = %d\n", rc);  	} -	_cifsFileInfo_put(cfile, false /* do not wait for ourself */); +	_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);  	cifs_done_oplock_break(cinode);  }  |