xref: /openbmc/linux/fs/afs/fsclient.c (revision 00d3b7a4)
108e0e7c8SDavid Howells /* AFS File Server client stubs
21da177e4SLinus Torvalds  *
308e0e7c8SDavid Howells  * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
41da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/init.h>
131da177e4SLinus Torvalds #include <linux/sched.h>
1408e0e7c8SDavid Howells #include <linux/circ_buf.h>
151da177e4SLinus Torvalds #include "internal.h"
1608e0e7c8SDavid Howells #include "afs_fs.h"
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds /*
1908e0e7c8SDavid Howells  * decode an AFSFetchStatus block
201da177e4SLinus Torvalds  */
2108e0e7c8SDavid Howells static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
2208e0e7c8SDavid Howells 				      struct afs_vnode *vnode)
231da177e4SLinus Torvalds {
2408e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
2508e0e7c8SDavid Howells 	umode_t mode;
2608e0e7c8SDavid Howells 	u64 data_version;
2708e0e7c8SDavid Howells 	u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
2808e0e7c8SDavid Howells 
2908e0e7c8SDavid Howells #define EXTRACT(DST)				\
3008e0e7c8SDavid Howells 	do {					\
3108e0e7c8SDavid Howells 		u32 x = ntohl(*bp++);		\
3208e0e7c8SDavid Howells 		changed |= DST - x;		\
3308e0e7c8SDavid Howells 		DST = x;			\
3408e0e7c8SDavid Howells 	} while (0)
3508e0e7c8SDavid Howells 
3608e0e7c8SDavid Howells 	vnode->status.if_version = ntohl(*bp++);
3708e0e7c8SDavid Howells 	EXTRACT(vnode->status.type);
3808e0e7c8SDavid Howells 	vnode->status.nlink = ntohl(*bp++);
3908e0e7c8SDavid Howells 	EXTRACT(vnode->status.size);
4008e0e7c8SDavid Howells 	data_version = ntohl(*bp++);
4108e0e7c8SDavid Howells 	EXTRACT(vnode->status.author);
4208e0e7c8SDavid Howells 	EXTRACT(vnode->status.owner);
4308e0e7c8SDavid Howells 	EXTRACT(vnode->status.caller_access); /* call ticket dependent */
4408e0e7c8SDavid Howells 	EXTRACT(vnode->status.anon_access);
4508e0e7c8SDavid Howells 	EXTRACT(vnode->status.mode);
4608e0e7c8SDavid Howells 	vnode->status.parent.vid = vnode->fid.vid;
4708e0e7c8SDavid Howells 	EXTRACT(vnode->status.parent.vnode);
4808e0e7c8SDavid Howells 	EXTRACT(vnode->status.parent.unique);
4908e0e7c8SDavid Howells 	bp++; /* seg size */
5008e0e7c8SDavid Howells 	vnode->status.mtime_client = ntohl(*bp++);
5108e0e7c8SDavid Howells 	vnode->status.mtime_server = ntohl(*bp++);
5208e0e7c8SDavid Howells 	bp++; /* group */
5308e0e7c8SDavid Howells 	bp++; /* sync counter */
5408e0e7c8SDavid Howells 	data_version |= (u64) ntohl(*bp++) << 32;
5508e0e7c8SDavid Howells 	bp++; /* spare2 */
5608e0e7c8SDavid Howells 	bp++; /* spare3 */
5708e0e7c8SDavid Howells 	bp++; /* spare4 */
5808e0e7c8SDavid Howells 	*_bp = bp;
5908e0e7c8SDavid Howells 
6008e0e7c8SDavid Howells 	if (changed) {
6108e0e7c8SDavid Howells 		_debug("vnode changed");
6208e0e7c8SDavid Howells 		set_bit(AFS_VNODE_CHANGED, &vnode->flags);
6308e0e7c8SDavid Howells 		vnode->vfs_inode.i_uid		= vnode->status.owner;
6408e0e7c8SDavid Howells 		vnode->vfs_inode.i_size		= vnode->status.size;
6508e0e7c8SDavid Howells 		vnode->vfs_inode.i_version	= vnode->fid.unique;
6608e0e7c8SDavid Howells 
6708e0e7c8SDavid Howells 		vnode->status.mode &= S_IALLUGO;
6808e0e7c8SDavid Howells 		mode = vnode->vfs_inode.i_mode;
6908e0e7c8SDavid Howells 		mode &= ~S_IALLUGO;
7008e0e7c8SDavid Howells 		mode |= vnode->status.mode;
7108e0e7c8SDavid Howells 		vnode->vfs_inode.i_mode = mode;
7208e0e7c8SDavid Howells 	}
7308e0e7c8SDavid Howells 
7408e0e7c8SDavid Howells 	_debug("vnode time %lx, %lx",
7508e0e7c8SDavid Howells 	       vnode->status.mtime_client, vnode->status.mtime_server);
7608e0e7c8SDavid Howells 	vnode->vfs_inode.i_ctime.tv_sec	= vnode->status.mtime_server;
7708e0e7c8SDavid Howells 	vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
7808e0e7c8SDavid Howells 	vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
7908e0e7c8SDavid Howells 
8008e0e7c8SDavid Howells 	if (vnode->status.data_version != data_version) {
8108e0e7c8SDavid Howells 		_debug("vnode modified %llx", data_version);
8208e0e7c8SDavid Howells 		vnode->status.data_version = data_version;
8308e0e7c8SDavid Howells 		set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
8408e0e7c8SDavid Howells 		set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
851da177e4SLinus Torvalds 	}
86ec26815aSDavid Howells }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds /*
8908e0e7c8SDavid Howells  * decode an AFSCallBack block
901da177e4SLinus Torvalds  */
9108e0e7c8SDavid Howells static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
921da177e4SLinus Torvalds {
9308e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
941da177e4SLinus Torvalds 
9508e0e7c8SDavid Howells 	vnode->cb_version	= ntohl(*bp++);
9608e0e7c8SDavid Howells 	vnode->cb_expiry	= ntohl(*bp++);
9708e0e7c8SDavid Howells 	vnode->cb_type		= ntohl(*bp++);
9808e0e7c8SDavid Howells 	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
9908e0e7c8SDavid Howells 	*_bp = bp;
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds /*
10308e0e7c8SDavid Howells  * decode an AFSVolSync block
1041da177e4SLinus Torvalds  */
10508e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
10608e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
1071da177e4SLinus Torvalds {
10808e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1091da177e4SLinus Torvalds 
11008e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
11108e0e7c8SDavid Howells 	bp++; /* spare2 */
11208e0e7c8SDavid Howells 	bp++; /* spare3 */
11308e0e7c8SDavid Howells 	bp++; /* spare4 */
11408e0e7c8SDavid Howells 	bp++; /* spare5 */
11508e0e7c8SDavid Howells 	bp++; /* spare6 */
11608e0e7c8SDavid Howells 	*_bp = bp;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
11908e0e7c8SDavid Howells /*
12008e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
12108e0e7c8SDavid Howells  */
12208e0e7c8SDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call,
12308e0e7c8SDavid Howells 				       struct sk_buff *skb, bool last)
12408e0e7c8SDavid Howells {
12508e0e7c8SDavid Howells 	const __be32 *bp;
1261da177e4SLinus Torvalds 
12708e0e7c8SDavid Howells 	_enter(",,%u", last);
1281da177e4SLinus Torvalds 
12908e0e7c8SDavid Howells 	afs_transfer_reply(call, skb);
13008e0e7c8SDavid Howells 	if (!last)
13108e0e7c8SDavid Howells 		return 0;
1321da177e4SLinus Torvalds 
13308e0e7c8SDavid Howells 	if (call->reply_size != call->reply_max)
13408e0e7c8SDavid Howells 		return -EBADMSG;
1351da177e4SLinus Torvalds 
13608e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
13708e0e7c8SDavid Howells 	bp = call->buffer;
13808e0e7c8SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply);
13908e0e7c8SDavid Howells 	xdr_decode_AFSCallBack(&bp, call->reply);
14008e0e7c8SDavid Howells 	if (call->reply2)
14108e0e7c8SDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply2);
1421da177e4SLinus Torvalds 
14308e0e7c8SDavid Howells 	_leave(" = 0 [done]");
14408e0e7c8SDavid Howells 	return 0;
145ec26815aSDavid Howells }
14608e0e7c8SDavid Howells 
14708e0e7c8SDavid Howells /*
14808e0e7c8SDavid Howells  * FS.FetchStatus operation type
14908e0e7c8SDavid Howells  */
15008e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
15100d3b7a4SDavid Howells 	.name		= "FS.FetchStatus",
15208e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
15308e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
15408e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
15508e0e7c8SDavid Howells };
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds /*
1581da177e4SLinus Torvalds  * fetch the status information for a file
1591da177e4SLinus Torvalds  */
16008e0e7c8SDavid Howells int afs_fs_fetch_file_status(struct afs_server *server,
16100d3b7a4SDavid Howells 			     struct key *key,
1621da177e4SLinus Torvalds 			     struct afs_vnode *vnode,
16308e0e7c8SDavid Howells 			     struct afs_volsync *volsync,
16408e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
1651da177e4SLinus Torvalds {
16608e0e7c8SDavid Howells 	struct afs_call *call;
1671da177e4SLinus Torvalds 	__be32 *bp;
1681da177e4SLinus Torvalds 
16900d3b7a4SDavid Howells 	_enter(",%x,,,", key_serial(key));
1701da177e4SLinus Torvalds 
17108e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, 120);
17208e0e7c8SDavid Howells 	if (!call)
17308e0e7c8SDavid Howells 		return -ENOMEM;
1741da177e4SLinus Torvalds 
17500d3b7a4SDavid Howells 	call->key = key;
17608e0e7c8SDavid Howells 	call->reply = vnode;
17708e0e7c8SDavid Howells 	call->reply2 = volsync;
17808e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
17908e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	/* marshall the parameters */
18208e0e7c8SDavid Howells 	bp = call->request;
1831da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
1841da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
1851da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
1861da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
1871da177e4SLinus Torvalds 
18808e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
189ec26815aSDavid Howells }
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds /*
19208e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
1931da177e4SLinus Torvalds  */
19408e0e7c8SDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call,
19508e0e7c8SDavid Howells 				     struct sk_buff *skb, bool last)
1961da177e4SLinus Torvalds {
19708e0e7c8SDavid Howells 	const __be32 *bp;
19808e0e7c8SDavid Howells 	struct page *page;
19908e0e7c8SDavid Howells 	void *buffer;
2001da177e4SLinus Torvalds 	int ret;
2011da177e4SLinus Torvalds 
20208e0e7c8SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
2031da177e4SLinus Torvalds 
20408e0e7c8SDavid Howells 	switch (call->unmarshall) {
20508e0e7c8SDavid Howells 	case 0:
20608e0e7c8SDavid Howells 		call->offset = 0;
20708e0e7c8SDavid Howells 		call->unmarshall++;
2081da177e4SLinus Torvalds 
20908e0e7c8SDavid Howells 		/* extract the returned data length */
21008e0e7c8SDavid Howells 	case 1:
21108e0e7c8SDavid Howells 		_debug("extract data length");
21208e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
21308e0e7c8SDavid Howells 		switch (ret) {
21408e0e7c8SDavid Howells 		case 0:		break;
21508e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
21608e0e7c8SDavid Howells 		default:	return ret;
2171da177e4SLinus Torvalds 		}
2181da177e4SLinus Torvalds 
21908e0e7c8SDavid Howells 		call->count = ntohl(call->tmp);
22008e0e7c8SDavid Howells 		_debug("DATA length: %u", call->count);
22108e0e7c8SDavid Howells 		if (call->count > PAGE_SIZE)
22208e0e7c8SDavid Howells 			return -EBADMSG;
22308e0e7c8SDavid Howells 		call->offset = 0;
22408e0e7c8SDavid Howells 		call->unmarshall++;
2251da177e4SLinus Torvalds 
22608e0e7c8SDavid Howells 		if (call->count < PAGE_SIZE) {
22708e0e7c8SDavid Howells 			buffer = kmap_atomic(call->reply3, KM_USER0);
22808e0e7c8SDavid Howells 			memset(buffer + PAGE_SIZE - call->count, 0,
22908e0e7c8SDavid Howells 			       call->count);
23008e0e7c8SDavid Howells 			kunmap_atomic(buffer, KM_USER0);
2311da177e4SLinus Torvalds 		}
2321da177e4SLinus Torvalds 
23308e0e7c8SDavid Howells 		/* extract the returned data */
23408e0e7c8SDavid Howells 	case 2:
23508e0e7c8SDavid Howells 		_debug("extract data");
23608e0e7c8SDavid Howells 		page = call->reply3;
23708e0e7c8SDavid Howells 		buffer = kmap_atomic(page, KM_USER0);
23808e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, buffer, call->count);
23908e0e7c8SDavid Howells 		kunmap_atomic(buffer, KM_USER0);
24008e0e7c8SDavid Howells 		switch (ret) {
24108e0e7c8SDavid Howells 		case 0:		break;
24208e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
24308e0e7c8SDavid Howells 		default:	return ret;
2441da177e4SLinus Torvalds 		}
2451da177e4SLinus Torvalds 
24608e0e7c8SDavid Howells 		call->offset = 0;
24708e0e7c8SDavid Howells 		call->unmarshall++;
24808e0e7c8SDavid Howells 
24908e0e7c8SDavid Howells 		/* extract the metadata */
25008e0e7c8SDavid Howells 	case 3:
25108e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer, 120);
25208e0e7c8SDavid Howells 		switch (ret) {
25308e0e7c8SDavid Howells 		case 0:		break;
25408e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
25508e0e7c8SDavid Howells 		default:	return ret;
256ec26815aSDavid Howells 		}
2571da177e4SLinus Torvalds 
25808e0e7c8SDavid Howells 		bp = call->buffer;
25908e0e7c8SDavid Howells 		xdr_decode_AFSFetchStatus(&bp, call->reply);
26008e0e7c8SDavid Howells 		xdr_decode_AFSCallBack(&bp, call->reply);
26108e0e7c8SDavid Howells 		if (call->reply2)
26208e0e7c8SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply2);
2631da177e4SLinus Torvalds 
26408e0e7c8SDavid Howells 		call->offset = 0;
26508e0e7c8SDavid Howells 		call->unmarshall++;
2661da177e4SLinus Torvalds 
26708e0e7c8SDavid Howells 	case 4:
26808e0e7c8SDavid Howells 		_debug("trailer");
26908e0e7c8SDavid Howells 		if (skb->len != 0)
27008e0e7c8SDavid Howells 			return -EBADMSG;
2711da177e4SLinus Torvalds 		break;
2721da177e4SLinus Torvalds 	}
2731da177e4SLinus Torvalds 
27408e0e7c8SDavid Howells 	if (!last)
27508e0e7c8SDavid Howells 		return 0;
2761da177e4SLinus Torvalds 
27708e0e7c8SDavid Howells 	_leave(" = 0 [done]");
27808e0e7c8SDavid Howells 	return 0;
279ec26815aSDavid Howells }
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds /*
28208e0e7c8SDavid Howells  * FS.FetchData operation type
2831da177e4SLinus Torvalds  */
28408e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
28500d3b7a4SDavid Howells 	.name		= "FS.FetchData",
28608e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
28708e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
28808e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
28908e0e7c8SDavid Howells };
29008e0e7c8SDavid Howells 
29108e0e7c8SDavid Howells /*
29208e0e7c8SDavid Howells  * fetch data from a file
29308e0e7c8SDavid Howells  */
29408e0e7c8SDavid Howells int afs_fs_fetch_data(struct afs_server *server,
29500d3b7a4SDavid Howells 		      struct key *key,
2961da177e4SLinus Torvalds 		      struct afs_vnode *vnode,
29708e0e7c8SDavid Howells 		      off_t offset, size_t length,
29808e0e7c8SDavid Howells 		      struct page *buffer,
29908e0e7c8SDavid Howells 		      struct afs_volsync *volsync,
30008e0e7c8SDavid Howells 		      const struct afs_wait_mode *wait_mode)
3011da177e4SLinus Torvalds {
30208e0e7c8SDavid Howells 	struct afs_call *call;
30308e0e7c8SDavid Howells 	__be32 *bp;
3041da177e4SLinus Torvalds 
30508e0e7c8SDavid Howells 	_enter("");
3061da177e4SLinus Torvalds 
30708e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, 120);
30808e0e7c8SDavid Howells 	if (!call)
30908e0e7c8SDavid Howells 		return -ENOMEM;
3101da177e4SLinus Torvalds 
31100d3b7a4SDavid Howells 	call->key = key;
31208e0e7c8SDavid Howells 	call->reply = vnode;
31308e0e7c8SDavid Howells 	call->reply2 = volsync;
31408e0e7c8SDavid Howells 	call->reply3 = buffer;
31508e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
31608e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	/* marshall the parameters */
31908e0e7c8SDavid Howells 	bp = call->request;
32008e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
32108e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
32208e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
32308e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
32408e0e7c8SDavid Howells 	bp[4] = htonl(offset);
32508e0e7c8SDavid Howells 	bp[5] = htonl(length);
3261da177e4SLinus Torvalds 
32708e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
33008e0e7c8SDavid Howells /*
33108e0e7c8SDavid Howells  * deliver reply data to an FS.GiveUpCallBacks
33208e0e7c8SDavid Howells  */
33308e0e7c8SDavid Howells static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
33408e0e7c8SDavid Howells 					    struct sk_buff *skb, bool last)
33508e0e7c8SDavid Howells {
33608e0e7c8SDavid Howells 	_enter(",{%u},%d", skb->len, last);
3371da177e4SLinus Torvalds 
33808e0e7c8SDavid Howells 	if (skb->len > 0)
33908e0e7c8SDavid Howells 		return -EBADMSG; /* shouldn't be any reply data */
34008e0e7c8SDavid Howells 	return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
34308e0e7c8SDavid Howells /*
34408e0e7c8SDavid Howells  * FS.GiveUpCallBacks operation type
34508e0e7c8SDavid Howells  */
34608e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
34700d3b7a4SDavid Howells 	.name		= "FS.GiveUpCallBacks",
34808e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_give_up_callbacks,
34908e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
35008e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
35108e0e7c8SDavid Howells };
3521da177e4SLinus Torvalds 
35308e0e7c8SDavid Howells /*
35408e0e7c8SDavid Howells  * give up a set of callbacks
35508e0e7c8SDavid Howells  * - the callbacks are held in the server->cb_break ring
35608e0e7c8SDavid Howells  */
35708e0e7c8SDavid Howells int afs_fs_give_up_callbacks(struct afs_server *server,
35808e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
35908e0e7c8SDavid Howells {
36008e0e7c8SDavid Howells 	struct afs_call *call;
36108e0e7c8SDavid Howells 	size_t ncallbacks;
36208e0e7c8SDavid Howells 	__be32 *bp, *tp;
36308e0e7c8SDavid Howells 	int loop;
3641da177e4SLinus Torvalds 
36508e0e7c8SDavid Howells 	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
36608e0e7c8SDavid Howells 			      ARRAY_SIZE(server->cb_break));
36708e0e7c8SDavid Howells 
36808e0e7c8SDavid Howells 	_enter("{%zu},", ncallbacks);
36908e0e7c8SDavid Howells 
37008e0e7c8SDavid Howells 	if (ncallbacks == 0)
37108e0e7c8SDavid Howells 		return 0;
37208e0e7c8SDavid Howells 	if (ncallbacks > AFSCBMAX)
37308e0e7c8SDavid Howells 		ncallbacks = AFSCBMAX;
37408e0e7c8SDavid Howells 
37508e0e7c8SDavid Howells 	_debug("break %zu callbacks", ncallbacks);
37608e0e7c8SDavid Howells 
37708e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
37808e0e7c8SDavid Howells 				   12 + ncallbacks * 6 * 4, 0);
37908e0e7c8SDavid Howells 	if (!call)
38008e0e7c8SDavid Howells 		return -ENOMEM;
38108e0e7c8SDavid Howells 
38208e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
38308e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
38408e0e7c8SDavid Howells 
38508e0e7c8SDavid Howells 	/* marshall the parameters */
38608e0e7c8SDavid Howells 	bp = call->request;
38708e0e7c8SDavid Howells 	tp = bp + 2 + ncallbacks * 3;
38808e0e7c8SDavid Howells 	*bp++ = htonl(FSGIVEUPCALLBACKS);
38908e0e7c8SDavid Howells 	*bp++ = htonl(ncallbacks);
39008e0e7c8SDavid Howells 	*tp++ = htonl(ncallbacks);
39108e0e7c8SDavid Howells 
39208e0e7c8SDavid Howells 	atomic_sub(ncallbacks, &server->cb_break_n);
39308e0e7c8SDavid Howells 	for (loop = ncallbacks; loop > 0; loop--) {
39408e0e7c8SDavid Howells 		struct afs_callback *cb =
39508e0e7c8SDavid Howells 			&server->cb_break[server->cb_break_tail];
39608e0e7c8SDavid Howells 
39708e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vid);
39808e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vnode);
39908e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.unique);
40008e0e7c8SDavid Howells 		*tp++ = htonl(cb->version);
40108e0e7c8SDavid Howells 		*tp++ = htonl(cb->expiry);
40208e0e7c8SDavid Howells 		*tp++ = htonl(cb->type);
40308e0e7c8SDavid Howells 		smp_mb();
40408e0e7c8SDavid Howells 		server->cb_break_tail =
40508e0e7c8SDavid Howells 			(server->cb_break_tail + 1) &
40608e0e7c8SDavid Howells 			(ARRAY_SIZE(server->cb_break) - 1);
407ec26815aSDavid Howells 	}
40808e0e7c8SDavid Howells 
40908e0e7c8SDavid Howells 	ASSERT(ncallbacks > 0);
41008e0e7c8SDavid Howells 	wake_up_nr(&server->cb_break_waitq, ncallbacks);
41108e0e7c8SDavid Howells 
41208e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
41308e0e7c8SDavid Howells }
414