Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 87182759 authored by David Howells's avatar David Howells
Browse files

afs: Fix order-1 allocation in afs_do_lookup()



afs_do_lookup() will do an order-1 allocation to allocate status records if
there are more than 39 vnodes to stat.

Fix this by allocating an array of {status,callback} records for each vnode
we want to examine using vmalloc() if larger than a page.

This not only gets rid of the order-1 allocation, but makes it easier to
grow beyond 50 records for YFS servers.  It also allows us to move to
{status,callback} tuples for other calls too and makes it easier to lock
across the application of the status and the callback to the vnode.

Fixes: 5cf9dd55 ("afs: Prospectively look up extra files when doing a single lookup")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent ffba718e
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -147,6 +147,12 @@ struct afs_file_status {
	u32			abort_code;	/* Abort if bulk-fetching this failed */
};

struct afs_status_cb {
	struct afs_file_status	status;
	struct afs_callback	callback;
	bool			have_cb;	/* True if cb record was retrieved */
};

/*
 * AFS file status change request
 */
+16 −21
Original line number Diff line number Diff line
@@ -102,8 +102,7 @@ struct afs_lookup_cookie {
	bool			found;
	bool			one_only;
	unsigned short		nr_fids;
	struct afs_file_status	*statuses;
	struct afs_callback	*callbacks;
	struct afs_status_cb	*statuses;
	struct afs_fid		fids[50];
};

@@ -640,6 +639,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
	struct afs_lookup_cookie *cookie;
	struct afs_cb_interest *cbi = NULL;
	struct afs_super_info *as = dir->i_sb->s_fs_info;
	struct afs_status_cb *scb;
	struct afs_iget_data data;
	struct afs_fs_cursor fc;
	struct afs_vnode *dvnode = AFS_FS_I(dir);
@@ -686,16 +686,11 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,

	/* Need space for examining all the selected files */
	inode = ERR_PTR(-ENOMEM);
	cookie->statuses = kcalloc(cookie->nr_fids, sizeof(struct afs_file_status),
	cookie->statuses = kvcalloc(cookie->nr_fids, sizeof(struct afs_status_cb),
				    GFP_KERNEL);
	if (!cookie->statuses)
		goto out;

	cookie->callbacks = kcalloc(cookie->nr_fids, sizeof(struct afs_callback),
				    GFP_KERNEL);
	if (!cookie->callbacks)
		goto out_s;

	/* Try FS.InlineBulkStatus first.  Abort codes for the individual
	 * lookups contained therein are stored in the reply without aborting
	 * the whole operation.
@@ -716,7 +711,6 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
						  afs_v2net(dvnode),
						  cookie->fids,
						  cookie->statuses,
						  cookie->callbacks,
						  cookie->nr_fids, NULL);
		}

@@ -741,11 +735,12 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
	inode = ERR_PTR(-ERESTARTSYS);
	if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
		while (afs_select_fileserver(&fc)) {
			scb = &cookie->statuses[0];
			afs_fs_fetch_status(&fc,
					    afs_v2net(dvnode),
					    cookie->fids,
					    cookie->statuses,
					    cookie->callbacks,
					    &scb->status,
					    &scb->callback,
					    NULL);
		}

@@ -758,24 +753,26 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
		goto out_c;

	for (i = 0; i < cookie->nr_fids; i++)
		cookie->statuses[i].abort_code = 0;
		cookie->statuses[i].status.abort_code = 0;

success:
	/* Turn all the files into inodes and save the first one - which is the
	 * one we actually want.
	 */
	if (cookie->statuses[0].abort_code != 0)
		inode = ERR_PTR(afs_abort_to_error(cookie->statuses[0].abort_code));
	scb = &cookie->statuses[0];
	if (scb->status.abort_code != 0)
		inode = ERR_PTR(afs_abort_to_error(scb->status.abort_code));

	for (i = 0; i < cookie->nr_fids; i++) {
		struct afs_status_cb *scb = &cookie->statuses[i];
		struct inode *ti;

		if (cookie->statuses[i].abort_code != 0)
		if (scb->status.abort_code != 0)
			continue;

		ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
			      &cookie->statuses[i],
			      &cookie->callbacks[i],
			      &scb->status,
			      &scb->callback,
			      cbi, dvnode);
		if (i == 0) {
			inode = ti;
@@ -787,9 +784,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,

out_c:
	afs_put_cb_interest(afs_v2net(dvnode), cbi);
	kfree(cookie->callbacks);
out_s:
	kfree(cookie->statuses);
	kvfree(cookie->statuses);
out:
	kfree(cookie);
	return inode;
+9 −12
Original line number Diff line number Diff line
@@ -2209,8 +2209,7 @@ int afs_fs_fetch_status(struct afs_fs_cursor *fc,
 */
static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
{
	struct afs_file_status *statuses;
	struct afs_callback *callbacks;
	struct afs_status_cb *scb;
	const __be32 *bp;
	u32 tmp;
	int ret;
@@ -2249,8 +2248,8 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
			return ret;

		bp = call->buffer;
		statuses = call->out_extra_status;
		ret = afs_decode_status(call, &bp, &statuses[call->count],
		scb = &call->out_scb[call->count];
		ret = afs_decode_status(call, &bp, &scb->status,
					NULL, NULL, NULL);
		if (ret < 0)
			return ret;
@@ -2290,9 +2289,9 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)

		_debug("unmarshall CB array");
		bp = call->buffer;
		callbacks = call->out_cb;
		xdr_decode_AFSCallBack_raw(call, &callbacks[call->count], &bp);
		statuses = call->out_extra_status;
		scb = &call->out_scb[call->count];
		xdr_decode_AFSCallBack_raw(call, &scb->callback, &bp);
		scb->have_cb = true;
		call->count++;
		if (call->count < call->count2)
			goto more_cbs;
@@ -2335,8 +2334,7 @@ static const struct afs_call_type afs_RXFSInlineBulkStatus = {
int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
			      struct afs_net *net,
			      struct afs_fid *fids,
			      struct afs_file_status *statuses,
			      struct afs_callback *callbacks,
			      struct afs_status_cb *statuses,
			      unsigned int nr_fids,
			      struct afs_volsync *volsync)
{
@@ -2345,7 +2343,7 @@ int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
	int i;

	if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags))
		return yfs_fs_inline_bulk_status(fc, net, fids, statuses, callbacks,
		return yfs_fs_inline_bulk_status(fc, net, fids, statuses,
						 nr_fids, volsync);

	_enter(",%x,{%llx:%llu},%u",
@@ -2360,8 +2358,7 @@ int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
	}

	call->key = fc->key;
	call->out_extra_status = statuses;
	call->out_cb = callbacks;
	call->out_scb = statuses;
	call->out_volsync = volsync;
	call->count2 = nr_fids;
	call->want_reply_time = true;
+5 −6
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ struct afs_call {
	struct afs_file_status	*out_vnode_status;
	struct afs_file_status	*out_extra_status;
	struct afs_callback	*out_cb;
	struct afs_status_cb	*out_scb;
	struct yfs_acl		*out_yacl;
	struct afs_volsync	*out_volsync;
	struct afs_volume_status *out_volstatus;
@@ -991,9 +992,8 @@ extern struct afs_call *afs_fs_get_capabilities(struct afs_net *, struct afs_ser
						struct afs_addr_cursor *, struct key *,
						unsigned int);
extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
				     struct afs_fid *, struct afs_file_status *,
				     struct afs_callback *, unsigned int,
				     struct afs_volsync *);
				     struct afs_fid *, struct afs_status_cb *,
				     unsigned int, struct afs_volsync *);
extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
			       struct afs_fid *, struct afs_file_status *,
			       struct afs_callback *, struct afs_volsync *);
@@ -1393,9 +1393,8 @@ extern int yfs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
			       struct afs_fid *, struct afs_file_status *,
			       struct afs_callback *, struct afs_volsync *);
extern int yfs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
				     struct afs_fid *, struct afs_file_status *,
				     struct afs_callback *, unsigned int,
				     struct afs_volsync *);
				     struct afs_fid *, struct afs_status_cb *,
				     unsigned int, struct afs_volsync *);

struct yfs_acl {
	struct afs_acl	*acl;		/* Dir/file/symlink ACL */
+9 −11
Original line number Diff line number Diff line
@@ -2032,8 +2032,7 @@ int yfs_fs_fetch_status(struct afs_fs_cursor *fc,
 */
static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call)
{
	struct afs_file_status *statuses;
	struct afs_callback *callbacks;
	struct afs_status_cb *scb;
	const __be32 *bp;
	u32 tmp;
	int ret;
@@ -2072,8 +2071,8 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call)
			return ret;

		bp = call->buffer;
		statuses = call->out_extra_status;
		ret = yfs_decode_status(call, &bp, &statuses[call->count],
		scb = &call->out_scb[call->count];
		ret = yfs_decode_status(call, &bp, &scb->status,
					NULL, NULL, NULL);
		if (ret < 0)
			return ret;
@@ -2113,8 +2112,9 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call)

		_debug("unmarshall CB array");
		bp = call->buffer;
		callbacks = call->out_cb;
		xdr_decode_YFSCallBack_raw(call, &callbacks[call->count], &bp);
		scb = &call->out_scb[call->count];
		xdr_decode_YFSCallBack_raw(call, &scb->callback, &bp);
		scb->have_cb = true;
		call->count++;
		if (call->count < call->count2)
			goto more_cbs;
@@ -2158,8 +2158,7 @@ static const struct afs_call_type yfs_RXYFSInlineBulkStatus = {
int yfs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
			      struct afs_net *net,
			      struct afs_fid *fids,
			      struct afs_file_status *statuses,
			      struct afs_callback *callbacks,
			      struct afs_status_cb *statuses,
			      unsigned int nr_fids,
			      struct afs_volsync *volsync)
{
@@ -2182,8 +2181,7 @@ int yfs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
	}

	call->key = fc->key;
	call->out_extra_status = statuses;
	call->out_cb = callbacks;
	call->out_scb = statuses;
	call->out_volsync = volsync;
	call->count2 = nr_fids;