Ejemplo n.º 1
0
/*
 * Fetch file status from the volume.
 */
int afs_fetch_status(struct afs_vnode *vnode, struct key *key)
{
	struct afs_fs_cursor fc;
	int ret;

	_enter("%s,{%x:%u.%u,S=%lx}",
	       vnode->volume->name,
	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
	       vnode->flags);

	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, vnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = vnode->cb_break + vnode->cb_s_break;
			afs_fs_fetch_file_status(&fc, NULL);
		}

		afs_check_for_remote_deletion(&fc, fc.vnode);
		afs_vnode_commit_status(&fc, vnode, fc.cb_break);
		ret = afs_end_vnode_operation(&fc);
	}

	_leave(" = %d", ret);
	return ret;
}
Ejemplo n.º 2
0
/*
 * fetch file status from the volume
 * - don't issue a fetch if:
 *   - the changed bit is not set and there's a valid callback
 *   - there are any outstanding ops that will fetch the status
 * - TODO implement local caching
 */
int afs_vnode_fetch_status(struct afs_vnode *vnode,
			   struct afs_vnode *auth_vnode, struct key *key)
{
	struct afs_server *server;
	unsigned long acl_order;
	int ret;

	DECLARE_WAITQUEUE(myself, current);

	_enter("%s,{%x:%u.%u}",
	       vnode->volume->vlocation->vldb.name,
	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);

	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
	    vnode->cb_promised) {
		_leave(" [unchanged]");
		return 0;
	}

	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
		_leave(" [deleted]");
		return -ENOENT;
	}

	acl_order = 0;
	if (auth_vnode)
		acl_order = auth_vnode->acl_order;

	spin_lock(&vnode->lock);

	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
	    vnode->cb_promised) {
		spin_unlock(&vnode->lock);
		_leave(" [unchanged]");
		return 0;
	}

	ASSERTCMP(vnode->update_cnt, >=, 0);

	if (vnode->update_cnt > 0) {
		/* someone else started a fetch */
		_debug("wait on fetch %d", vnode->update_cnt);

		set_current_state(TASK_UNINTERRUPTIBLE);
		ASSERT(myself.func != NULL);
		add_wait_queue(&vnode->update_waitq, &myself);

		/* wait for the status to be updated */
		for (;;) {
			if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
				break;
			if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
				break;

			/* check to see if it got updated and invalidated all
			 * before we saw it */
			if (vnode->update_cnt == 0) {
				remove_wait_queue(&vnode->update_waitq,
						  &myself);
				set_current_state(TASK_RUNNING);
				goto get_anyway;
			}

			spin_unlock(&vnode->lock);

			schedule();
			set_current_state(TASK_UNINTERRUPTIBLE);

			spin_lock(&vnode->lock);
		}

		remove_wait_queue(&vnode->update_waitq, &myself);
		spin_unlock(&vnode->lock);
		set_current_state(TASK_RUNNING);

		return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
			-ENOENT : 0;
	}

get_anyway:
	/* okay... we're going to have to initiate the op */
	vnode->update_cnt++;

	spin_unlock(&vnode->lock);

	/* merge AFS status fetches and clear outstanding callback on this
	 * vnode */
	do {
		/* pick a server to query */
		server = afs_volume_pick_fileserver(vnode);
		if (IS_ERR(server))
			goto no_server;

		_debug("USING SERVER: %p{%08x}",
		       server, ntohl(server->addr.s_addr));

		ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
					       &afs_sync_call);

	} while (!afs_volume_release_fileserver(vnode, server, ret));

	/* adjust the flags */
	if (ret == 0) {
		_debug("adjust");
		if (auth_vnode)
			afs_cache_permit(vnode, key, acl_order);
		afs_vnode_finalise_status_update(vnode, server);
		afs_put_server(server);
	} else {
		_debug("failed [%d]", ret);
		afs_vnode_status_update_failed(vnode, ret);
	}

	ASSERTCMP(vnode->update_cnt, >=, 0);

	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
	return ret;

no_server:
	spin_lock(&vnode->lock);
	vnode->update_cnt--;
	ASSERTCMP(vnode->update_cnt, >=, 0);
	spin_unlock(&vnode->lock);
	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
	return PTR_ERR(server);
}