2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Implementation of the client side of the AFS File Server extended
13 * statistics facility.
15 *------------------------------------------------------------------------*/
17 #include <afsconfig.h>
18 #include <afs/param.h>
22 #include "xstat_fs.h" /*Interface for this module */
23 #include <lwp.h> /*Lightweight process package */
25 #include <afs/afsutil.h>
26 #include <afs/afscbint.h>
28 #define LWP_STACK_SIZE (16 * 1024)
33 int xstat_fs_numServers; /*Num connected servers */
34 struct xstat_fs_ConnectionInfo
35 *xstat_fs_ConnInfo; /*Ptr to connection array */
36 int numCollections; /*Number of data collections */
37 struct xstat_fs_ProbeResults xstat_fs_Results; /*Latest probe results */
38 char terminationEvent; /*One-shot termination event */
40 afs_int32 xstat_fsData[AFS_MAX_XSTAT_LONGS]; /*Buffer for collected data */
45 static int xstat_fs_ProbeFreqInSecs; /*Probe freq. in seconds */
46 static int xstat_fs_initflag = 0; /*Was init routine called? */
47 static int xstat_fs_debug = 0; /*Debugging output enabled? */
48 static int xstat_fs_oneShot = 0; /*One-shot operation? */
49 static int (*xstat_fs_Handler) (void); /*Probe handler routine */
50 static PROCESS probeLWP_ID; /*Probe LWP process ID */
51 static int xstat_fs_numCollections; /*Number of desired collections */
52 static afs_int32 *xstat_fs_collIDP; /*Ptr to collection IDs desired */
55 * We have to pass a port to Rx to start up our callback listener
56 * service, but 7001 is already taken up by the Cache Manager. So,
59 #define XSTAT_FS_CBPORT 7101
62 /*------------------------------------------------------------------------
63 * [private] xstat_fs_CleanupInit
66 * Set up for recovery after an error in initialization (i.e.,
67 * during a call to xstat_fs_Init.
74 * Error value otherwise.
77 * This routine is private to the module.
80 * Zeros out basic data structures.
81 *------------------------------------------------------------------------*/
84 xstat_fs_CleanupInit(void)
86 afs_int32 code; /*Return code from callback stubs */
87 struct rx_call *rxcall; /*Bogus param */
88 AFSCBFids *Fids_Array; /*Bogus param */
89 AFSCBs *CallBack_Array; /*Bogus param */
91 xstat_fs_ConnInfo = (struct xstat_fs_ConnectionInfo *)0;
92 xstat_fs_Results.probeNum = 0;
93 xstat_fs_Results.probeTime = 0;
94 xstat_fs_Results.connP = (struct xstat_fs_ConnectionInfo *)0;
95 xstat_fs_Results.collectionNumber = 0;
96 xstat_fs_Results.data.AFS_CollData_len = AFS_MAX_XSTAT_LONGS;
97 xstat_fs_Results.data.AFS_CollData_val = (afs_int32 *) xstat_fsData;
98 xstat_fs_Results.probeOK = 0;
100 rxcall = (struct rx_call *)0;
101 Fids_Array = (AFSCBFids *) 0;
102 CallBack_Array = (AFSCBs *) 0;
105 * Call each of the callback routines our module provides (in
106 * xstat_fs_callback.c) to make sure they're all there.
108 code = SRXAFSCB_CallBack(rxcall, Fids_Array, CallBack_Array);
111 code = SRXAFSCB_InitCallBackState3(rxcall, (afsUUID *) 0);
114 code = SRXAFSCB_Probe(rxcall);
119 /*------------------------------------------------------------------------
120 * [exported] xstat_fs_Cleanup
123 * Clean up our memory and connection state.
126 * int a_releaseMem : Should we free up malloc'ed areas?
129 * 0 on total success,
130 * -1 if the module was never initialized, or there was a problem
131 * with the xstat_fs connection array.
134 * xstat_fs_numServers should be properly set. We don't do anything
135 * unless xstat_fs_Init() has already been called.
138 * Shuts down Rx connections gracefully, frees allocated space
140 *------------------------------------------------------------------------*/
143 xstat_fs_Cleanup(int a_releaseMem)
145 static char rn[] = "xstat_fs_Cleanup"; /*Routine name */
146 int code; /*Return code */
147 int conn_idx; /*Current connection index */
148 struct xstat_fs_ConnectionInfo *curr_conn; /*Ptr to xstat_fs connection */
151 * Assume the best, but check the worst.
153 if (!xstat_fs_initflag) {
154 fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
160 * Take care of all Rx connections first. Check to see that the
161 * server count is a legal value.
163 if (xstat_fs_numServers <= 0) {
165 "[%s] Illegal number of servers (xstat_fs_numServers = %d)\n",
166 rn, xstat_fs_numServers);
169 if (xstat_fs_ConnInfo != (struct xstat_fs_ConnectionInfo *)0) {
171 * The xstat_fs connection structure array exists. Go through
172 * it and close up any Rx connections it holds.
174 curr_conn = xstat_fs_ConnInfo;
175 for (conn_idx = 0; conn_idx < xstat_fs_numServers; conn_idx++) {
176 if (curr_conn->rxconn != (struct rx_connection *)0) {
177 rx_DestroyConnection(curr_conn->rxconn);
178 curr_conn->rxconn = (struct rx_connection *)0;
181 } /*for each xstat_fs connection */
182 } /*xstat_fs connection structure exists */
183 } /*Legal number of servers */
186 * If asked to, release the space we've allocated.
189 if (xstat_fs_ConnInfo != (struct xstat_fs_ConnectionInfo *)0)
190 free(xstat_fs_ConnInfo);
194 * Return the news, whatever it is.
200 /*------------------------------------------------------------------------
201 * [private] xstat_fs_LWP
204 * This LWP iterates over the server connections and gathers up
205 * the desired statistics from each one on a regular basis. When
206 * the sweep is done, the associated handler function is called
207 * to process the new data.
216 * Started by xstat_fs_Init(), uses global structures and the
217 * global private xstat_fs_oneShot variable.
220 * Nothing interesting.
221 *------------------------------------------------------------------------*/
224 xstat_fs_LWP(void *unused)
226 static char rn[] = "xstat_fs_LWP"; /*Routine name */
227 afs_int32 code; /*Results of calls */
228 int oneShotCode; /*Result of one-shot signal */
229 struct timeval tv; /*Time structure */
230 int conn_idx; /*Connection index */
231 struct xstat_fs_ConnectionInfo *curr_conn; /*Current connection */
232 afs_int32 srvVersionNumber; /*Xstat version # */
233 afs_int32 clientVersionNumber; /*Client xstat version */
234 afs_int32 numColls; /*Number of collections to get */
235 afs_int32 *currCollIDP; /*Curr collection ID desired */
238 * Set up some numbers we'll need.
240 clientVersionNumber = AFS_XSTAT_VERSION;
242 while (1) { /*Service loop */
244 * Iterate through the server connections, gathering data.
245 * Don't forget to bump the probe count and zero the statistics
246 * areas before calling the servers.
249 printf("[%s] Waking up, getting data from %d server(s)\n", rn,
250 xstat_fs_numServers);
251 curr_conn = xstat_fs_ConnInfo;
252 xstat_fs_Results.probeNum++;
254 for (conn_idx = 0; conn_idx < xstat_fs_numServers; conn_idx++) {
256 * Grab the statistics for the current File Server, if the
257 * connection is valid.
260 printf("[%s] Getting collections from File Server '%s'\n", rn,
261 curr_conn->hostName);
262 if (curr_conn->rxconn != (struct rx_connection *)0) {
264 printf("[%s] Connection OK, calling RXAFS_GetXStats\n",
267 currCollIDP = xstat_fs_collIDP;
268 for (numColls = 0; numColls < xstat_fs_numCollections;
269 numColls++, currCollIDP++) {
271 * Initialize the per-probe values.
274 printf("[%s] Asking for data collection %d\n", rn,
276 xstat_fs_Results.collectionNumber = *currCollIDP;
277 xstat_fs_Results.data.AFS_CollData_len =
279 memset(xstat_fs_Results.data.AFS_CollData_val, 0,
280 AFS_MAX_XSTAT_LONGS * 4);
282 xstat_fs_Results.connP = curr_conn;
284 if (xstat_fs_debug) {
286 ("%s: Calling RXAFS_GetXStats, conn=%" AFS_PTR_FMT ", clientVersionNumber=%d, collectionNumber=%d, srvVersionNumberP=%" AFS_PTR_FMT ", timeP=%" AFS_PTR_FMT ", dataP=%" AFS_PTR_FMT "\n",
287 rn, curr_conn->rxconn, clientVersionNumber,
288 *currCollIDP, &srvVersionNumber,
289 &(xstat_fs_Results.probeTime),
290 &(xstat_fs_Results.data));
291 printf("%s: [bufflen=%d, buffer at %" AFS_PTR_FMT "]\n", rn,
292 xstat_fs_Results.data.AFS_CollData_len,
293 xstat_fs_Results.data.AFS_CollData_val);
296 xstat_fs_Results.probeOK =
297 RXAFS_GetXStats(curr_conn->rxconn,
298 clientVersionNumber, *currCollIDP,
300 &(xstat_fs_Results.probeTime),
301 &(xstat_fs_Results.data));
304 * Now that we (may) have the data for this connection,
305 * call the associated handler function. The handler does
306 * not take any explicit parameters, but rather gets to the
307 * goodies via some of the objects exported by this module.
310 printf("[%s] Calling handler routine.\n", rn);
311 code = xstat_fs_Handler();
314 "[%s] Handler returned error code %d\n", rn,
317 } /*For each collection */
320 /*Valid Rx connection */
322 * Advance the xstat_fs connection pointer.
326 } /*For each xstat_fs connection */
329 * All (valid) connections have been probed. Fall asleep for the
330 * prescribed number of seconds, unless we're a one-shot. In
331 * that case, we need to signal our caller that we're done.
334 printf("[%s] Polling complete for probe round %d.\n", rn,
335 xstat_fs_Results.probeNum);
337 if (xstat_fs_oneShot) {
339 * One-shot execution desired. Signal our main procedure
340 * that we've finished our collection round.
343 printf("[%s] Signalling main process at %" AFS_PTR_FMT "\n", rn,
345 oneShotCode = LWP_SignalProcess(&terminationEvent);
347 fprintf(stderr, "[%s] Error %d from LWP_SignalProcess()", rn,
349 break; /*from the perpetual while loop */
350 } /*One-shot execution */
353 * Continuous execution desired. Sleep for the required
356 tv.tv_sec = xstat_fs_ProbeFreqInSecs;
359 printf("[%s] Falling asleep for %d seconds\n", rn,
360 xstat_fs_ProbeFreqInSecs);
361 code = IOMGR_Select(0, /*Num fids */
362 0, /*Descs ready for reading */
363 0, /*Descs ready for writing */
364 0, /*Descs w/exceptional conditions */
365 &tv); /*Ptr to timeout structure */
367 fprintf(stderr, "[%s] IOMGR_Select returned code %d\n", rn,
369 } /*Continuous execution */
374 /*------------------------------------------------------------------------
375 * [exported] xstat_fs_Init
378 * Initialize the xstat_fs module: set up Rx connections to the
379 * given set of File Servers, start up the probe and callback LWPs,
380 * and associate the routine to be called when a probe completes.
381 * Also, let it know which collections you're interested in.
384 * int a_numServers : Num. servers to connect to.
385 * struct sockaddr_in *a_socketArray : Array of server sockets.
386 * int a_ProbeFreqInSecs : Probe frequency in seconds.
387 * int (*a_ProbeHandler)() : Ptr to probe handler fcn.
388 * int a_flags : Various flags.
389 * int a_numCollections : Number of collections desired.
390 * afs_int32 *a_collIDP : Ptr to collection IDs.
394 * -2 for (at least one) connection error,
395 * LWP process creation code, if it failed,
396 * -1 for other fatal errors.
399 * *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
400 * Also, the server security object CBsecobj MUST be a static,
401 * since it has to stick around after this routine exits.
404 * Sets up just about everything.
405 *------------------------------------------------------------------------*/
408 xstat_fs_Init(int a_numServers, struct sockaddr_in *a_socketArray,
409 int a_ProbeFreqInSecs, int (*a_ProbeHandler) (void), int a_flags,
410 int a_numCollections, afs_int32 * a_collIDP)
412 static char rn[] = "xstat_fs_Init"; /*Routine name */
413 afs_int32 code; /*Return value */
414 static struct rx_securityClass *CBsecobj; /*Callback security object */
415 struct rx_securityClass *secobj; /*Client security object */
416 struct rx_service *rxsrv_afsserver; /*Server for AFS */
417 int arg_errfound; /*Argument error found? */
418 int curr_srv; /*Current server idx */
419 struct xstat_fs_ConnectionInfo *curr_conn; /*Ptr to current conn */
420 char *hostNameFound; /*Ptr to returned host name */
421 int conn_err; /*Connection error? */
422 int PortToUse; /*Callback port to use */
423 int collIDBytes; /*Num bytes in coll ID array */
427 * If we've already been called, snicker at the bozo, gently
428 * remind him of his doubtful heritage, and return success.
430 if (xstat_fs_initflag) {
431 fprintf(stderr, "[%s] Called multiple times!\n", rn);
434 xstat_fs_initflag = 1;
437 * Check the parameters for bogosities.
440 if (a_numServers <= 0) {
441 fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
445 if (a_socketArray == (struct sockaddr_in *)0) {
446 fprintf(stderr, "[%s] Null server socket array argument\n", rn);
449 if (a_ProbeFreqInSecs <= 0) {
450 fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
454 if (a_ProbeHandler == (int (*)())0) {
455 fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
458 if (a_numCollections <= 0) {
459 fprintf(stderr, "[%s] Illegal collection count argument: %d\n", rn,
463 if (a_collIDP == NULL) {
464 fprintf(stderr, "[%s] Null collection ID array argument\n", rn);
471 * Record our passed-in info.
473 xstat_fs_debug = (a_flags & XSTAT_FS_INITFLAG_DEBUGGING);
474 xstat_fs_oneShot = (a_flags & XSTAT_FS_INITFLAG_ONE_SHOT);
475 xstat_fs_numServers = a_numServers;
476 xstat_fs_Handler = a_ProbeHandler;
477 xstat_fs_ProbeFreqInSecs = a_ProbeFreqInSecs;
478 xstat_fs_numCollections = a_numCollections;
479 collIDBytes = xstat_fs_numCollections * sizeof(afs_int32);
480 xstat_fs_collIDP = malloc(collIDBytes);
481 memcpy(xstat_fs_collIDP, a_collIDP, collIDBytes);
482 if (xstat_fs_debug) {
483 printf("[%s] Asking for %d collection(s): ", rn,
484 xstat_fs_numCollections);
485 for (curr_srv = 0; curr_srv < xstat_fs_numCollections; curr_srv++)
486 printf("%d ", *(xstat_fs_collIDP + curr_srv));
491 * Get ready in case we have to do a cleanup - basically, zero
494 code = xstat_fs_CleanupInit();
499 * Allocate the necessary data structures and initialize everything
502 xstat_fs_ConnInfo = malloc(a_numServers
503 * sizeof(struct xstat_fs_ConnectionInfo));
504 if (xstat_fs_ConnInfo == (struct xstat_fs_ConnectionInfo *)0) {
506 "[%s] Can't allocate %d connection info structs (%" AFS_SIZET_FMT " bytes)\n",
508 (a_numServers * sizeof(struct xstat_fs_ConnectionInfo)));
509 return (-1); /*No cleanup needs to be done yet */
513 * Initialize the Rx subsystem, just in case nobody's done it.
516 printf("[%s] Initializing Rx\n", rn);
517 PortToUse = XSTAT_FS_CBPORT;
520 code = rx_Init(htons(PortToUse));
522 if (code == RX_ADDRINUSE) {
525 "[%s] Callback port %d in use, advancing\n", rn,
529 fprintf(stderr, "[%s] Fatal error in rx_Init()\n", rn);
535 printf("[%s] Rx initialized on port %d\n", rn, PortToUse);
538 * Create a null Rx server security object, to be used by the
541 CBsecobj = (struct rx_securityClass *)
542 rxnull_NewServerSecurityObject();
543 if (CBsecobj == (struct rx_securityClass *)0) {
545 "[%s] Can't create callback listener's security object.\n",
547 xstat_fs_Cleanup(1); /*Delete already-malloc'ed areas */
551 printf("[%s] Callback server security object created\n", rn);
554 * Create a null Rx client security object, to be used by the
557 secobj = rxnull_NewClientSecurityObject();
558 if (secobj == (struct rx_securityClass *)0) {
560 "[%s] Can't create probe LWP client security object.\n", rn);
561 xstat_fs_Cleanup(1); /*Delete already-malloc'ed areas */
565 printf("[%s] Probe LWP client security object created\n", rn);
567 curr_conn = xstat_fs_ConnInfo;
569 for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
571 * Copy in the socket info for the current server, resolve its
572 * printable name if possible.
574 if (xstat_fs_debug) {
576 printf("[%s] Copying in the following socket info:\n", rn);
577 printf("[%s] IP addr %s, port %d\n", rn,
578 afs_inet_ntoa_r((a_socketArray + curr_srv)->sin_addr.s_addr,hoststr),
579 ntohs((a_socketArray + curr_srv)->sin_port));
581 memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
582 sizeof(struct sockaddr_in));
585 hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
586 if (hostNameFound == NULL) {
588 "[%s] Can't map Internet address %s to a string name\n",
589 rn, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
590 curr_conn->hostName[0] = '\0';
592 strcpy(curr_conn->hostName, hostNameFound);
594 printf("[%s] Host name for server index %d is %s\n", rn,
595 curr_srv, curr_conn->hostName);
599 * Make an Rx connection to the current server.
603 ("[%s] Connecting to srv idx %d, IP addr %s, port %d, service 1\n",
604 rn, curr_srv, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr),
605 ntohs(curr_conn->skt.sin_port));
607 curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr, /*Server addr */
608 curr_conn->skt.sin_port, /*Server port */
609 1, /*AFS service # */
610 secobj, /*Security obj */
612 if (curr_conn->rxconn == (struct rx_connection *)0) {
614 "[%s] Can't create Rx connection to server '%s' (%s)\n",
615 rn, curr_conn->hostName, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
619 printf("[%s] New connection at %" AFS_PTR_FMT "\n", rn, curr_conn->rxconn);
622 * Bump the current xstat_fs connection to set up.
629 * Create the AFS callback service (listener).
632 printf("[%s] Creating AFS callback listener\n", rn);
633 rxsrv_afsserver = rx_NewService(0, /*Use default port */
635 "afs", /*Service name */
636 &CBsecobj, /*Ptr to security object(s) */
637 1, /*# of security objects */
638 RXAFSCB_ExecuteRequest); /*Dispatcher */
639 if (rxsrv_afsserver == (struct rx_service *)0) {
640 fprintf(stderr, "[%s] Can't create callback Rx service/listener\n",
642 xstat_fs_Cleanup(1); /*Delete already-malloc'ed areas */
646 printf("[%s] Callback listener created\n", rn);
649 * Start up the AFS callback service.
652 printf("[%s] Starting up callback listener.\n", rn);
653 rx_StartServer(0); /*Don't donate yourself to LWP pool */
656 * Start up the probe LWP.
659 printf("[%s] Creating the probe LWP\n", rn);
660 code = LWP_CreateProcess(xstat_fs_LWP, /*Function to start up */
661 LWP_STACK_SIZE, /*Stack size in bytes */
663 (void *)0, /*Parameters */
664 "xstat_fs Worker", /*Name to use */
665 &probeLWP_ID); /*Returned LWP process ID */
667 fprintf(stderr, "[%s] Can't create xstat_fs LWP! Error is %d\n", rn,
669 xstat_fs_Cleanup(1); /*Delete already-malloc'ed areas */
673 printf("[%s] Probe LWP process structure located at %" AFS_PTR_FMT "\n", rn,
677 * Return the final results.
686 /*------------------------------------------------------------------------
687 * [exported] xstat_fs_ForceProbeNow
690 * Wake up the probe LWP, forcing it to execute a probe immediately.
697 * Error value otherwise.
700 * The module must have been initialized.
704 *------------------------------------------------------------------------*/
707 xstat_fs_ForceProbeNow(void)
709 static char rn[] = "xstat_fs_ForceProbeNow"; /*Routine name */
712 * There isn't a prayer unless we've been initialized.
714 if (!xstat_fs_initflag) {
715 fprintf(stderr, "[%s] Must call xstat_fs_Init first!\n", rn);
720 * Kick the sucker in the side.
722 IOMGR_Cancel(probeLWP_ID);
725 * We did it, so report the happy news.
731 * Fill the xstat full perf data structure from the data collection array.
733 * This function is a client-side decoding of the non-portable xstat_fs full
734 * performance data. The full perf structure includes timeval structures,
735 * which have platform dependent size.
737 * To make things even more interesting, the word ordering of the time
738 * values on hosts with 64-bit time depend on endianess. The ordering
739 * within a given afs_int32 is handled by xdr.
741 * @param[out] aout an address to a stats structure pointer
742 * @param[in] ain array of int32s received
743 * @param[in] alen length of ain
744 * @param[inout] abuf a buffer provided by the caller
746 * @return 0 on success
749 xstat_fs_DecodeFullPerfStats(struct fs_stats_FullPerfStats **aout,
752 struct fs_stats_FullPerfStats *abuf)
756 int snbo = -2; /* detected remote site has network-byte ordering */
758 static const int XSTAT_FPS_LEN = sizeof(struct fs_stats_FullPerfStats) / sizeof(afs_int32); /* local size of fps */
759 static const int XSTAT_FPS_SMALL = 424; /**< fps size when sizeof(timeval) is 2*sizeof(afs_int32) */
760 static const int XSTAT_FPS_LARGE = 666; /**< fps size when sizeof(timeval) is 2*sizeof(afs_int64) */
762 #define DECODE_TV(t) \
764 if (alen == XSTAT_FPS_SMALL) { \
766 (t).tv_usec = *p++; \
772 (t).tv_usec = *p++; \
776 (t).tv_usec = *p++; \
782 if (alen != XSTAT_FPS_SMALL && alen != XSTAT_FPS_LARGE) {
783 return -1; /* unrecognized size */
786 if (alen == XSTAT_FPS_LEN && alen == XSTAT_FPS_SMALL) {
787 /* Same size, and xdr dealt with byte ordering; no decoding needed. */
788 *aout = (struct fs_stats_FullPerfStats *)ain;
792 if (alen == XSTAT_FPS_LARGE) {
793 /* Attempt to detect the word ordering of the time values. */
794 struct fs_stats_FullPerfStats *fps =
795 (struct fs_stats_FullPerfStats *)ain;
796 afs_int32 *epoch = (afs_int32 *) & (fps->det.epoch);
797 if (epoch[0] == 0 && epoch[1] != 0) {
799 } else if (epoch[0] != 0 && epoch[1] == 0) {
802 return -2; /* failed to detect server word ordering */
806 if (alen == XSTAT_FPS_LEN && alen == XSTAT_FPS_LARGE
807 #if defined(WORDS_BIGENDIAN)
809 #else /* WORDS_BIGENDIAN */
811 #endif /* WORDS_BIGENDIAN */
813 /* Same size and order; no decoding needed. */
814 *aout = (struct fs_stats_FullPerfStats *)ain;
818 /* Either different sizes, or different ordering, or both. Schlep over
819 * each field, decoding time values. The fields up to the first time value
820 * can be copied in bulk. */
821 if (xstat_fs_debug) {
822 printf("debug: Decoding xstat full perf stats; length=%d", alen);
823 if (alen == XSTAT_FPS_LARGE) {
824 printf(", order='%s'", (snbo ? "big-endian" : "little-endian"));
830 memset(abuf, 0, sizeof(struct fs_stats_FullPerfStats));
831 memcpy(abuf, p, sizeof(struct afs_PerfStats));
832 p += sizeof(struct afs_PerfStats) / sizeof(afs_int32);
834 DECODE_TV(abuf->det.epoch);
835 for (i = 0; i < FS_STATS_NUM_RPC_OPS; i++) {
836 struct fs_stats_opTimingData *td = abuf->det.rpcOpTimes + i;
838 td->numSuccesses = *p++;
839 DECODE_TV(td->sumTime);
840 DECODE_TV(td->sqrTime);
841 DECODE_TV(td->minTime);
842 DECODE_TV(td->maxTime);
844 for (i = 0; i < FS_STATS_NUM_XFER_OPS; i++) {
845 struct fs_stats_xferData *xd = abuf->det.xferOpTimes + i;
847 xd->numSuccesses = *p++;
848 DECODE_TV(xd->sumTime);
849 DECODE_TV(xd->sqrTime);
850 DECODE_TV(xd->minTime);
851 DECODE_TV(xd->maxTime);
855 memcpy((void *)xd->count, (void *)p,
856 sizeof(afs_int32) * FS_STATS_NUM_XFER_BUCKETS);
857 p += FS_STATS_NUM_XFER_BUCKETS;