stats_log.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines for cachefs logging.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <libintl.h>
#include <time.h>
#include <string.h>
#include <malloc.h>
#include <limits.h>
#include "stats.h"
#include <assert.h>
/* forward declarations of statics */
static char *stats_log_fmtfid(cfs_fid_t *);
static int stats_log_fi_comp(const void *a, const void *b);
int
{
int error = 0;
int exists = 0;
goto out;
}
/*
* the stats_ API allows a NULL or an empty path to turn off
* logging, but the kstat interface has the string buffered,
* so we need to make an empty string.
*/
path = "";
goto out;
if (path[0] != '\0') {
struct stat64 s;
int f;
/* logfile will be <2GB */
if (f < 0) {
goto out;
}
if (fstat64(f, &s) < 0) {
gettext("Cannot stat logfile: %s"),
(void) close(f);
goto out;
}
/*
* the kernel will accept an empty file as a logfile. we must
* make sure that we created this empty file, i.e. that it's
* not an already existing file that happened to be empty.
*
* if we hand the kernel a nonempty file, it will check the
* magic number. thus, if they hand it something like
* catch the cases of empty files we don't want to be
* logfiles.
*/
"Cannot use existing empty file as a logfile"));
(void) close(f);
goto out;
}
(void) close(f);
}
gettext("Cannot set logfile path for this filesystem"));
goto out;
}
out:
return (error);
}
int
{
int error = 0;
goto out;
}
if (onoff)
else
gettext("Cannot set log bitmap for this filesystem"));
goto out;
}
out:
return (error);
}
char *
{
goto out;
out:
return (rc);
}
static kstat_t *
{
/*
* XXX if st was created for a particular cachedir, we
* should scan for another st->st_fsid that'll get us
* the same cache.
*/
gettext("Cannot lookup kstats for this filesystem"));
goto out;
}
gettext("Cannot read kstats for this filesystem"));
goto out;
}
out:
return (rc);
}
int
{
int rc = 0;
rc = -1;
goto out;
}
}
/* logfile will be <2GB */
rc = -1;
goto out;
}
rc = -1;
goto out;
}
rc = -1;
goto out;
}
rc = -1;
goto out;
}
out:
if (rc != 0) {
}
}
}
return (rc);
}
static bool_t
{
return (FALSE);
return (TRUE);
}
void *
{
int ttype;
gettext("Logfile was not open"));
goto out;
}
goto out;
switch (*type) {
struct cachefs_log_umount_record umount;
struct cachefs_log_getpage_record getpage;
struct cachefs_log_readdir_record readdir;
struct cachefs_log_readlink_record readlink;
struct cachefs_log_remove_record remove;
struct cachefs_log_rmdir_record rmdir;
struct cachefs_log_truncate_record truncate;
struct cachefs_log_putpage_record putpage;
struct cachefs_log_create_record create;
struct cachefs_log_mkdir_record mkdir;
struct cachefs_log_rename_record rename;
struct cachefs_log_symlink_record symlink;
struct cachefs_log_populate_record populate;
struct cachefs_log_csymlink_record csymlink;
struct cachefs_log_filldir_record filldir;
struct cachefs_log_mdcreate_record mdcreate;
struct cachefs_log_gpfront_record gpfront;
struct cachefs_log_rfdir_record rfdir;
struct cachefs_log_ualloc_record ualloc;
struct cachefs_log_calloc_record challoc;
struct cachefs_log_nocache_record nocache;
case CACHEFS_LOG_MOUNT:
gettext("Truncated mount record"));
goto out;
}
(struct cachefs_log_mount_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_UMOUNT:
gettext("Truncated umount record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_GETPAGE:
(! xdr_u_longlong_t(xdrs,
(! xdr_u_longlong_t(xdrs,
gettext("Truncated getpage record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_READDIR:
(! xdr_u_longlong_t(xdrs,
(! xdr_u_longlong_t(xdrs,
gettext("Truncated readdir record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_READLINK:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated readlink record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_REMOVE:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated remove record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_RMDIR:
gettext("Truncated rmdir record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_TRUNCATE:
(! xdr_u_longlong_t(xdrs,
(! xdr_u_longlong_t(xdrs,
gettext("Truncated truncate record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_PUTPAGE:
(! xdr_u_longlong_t(xdrs,
(! xdr_u_longlong_t(xdrs,
gettext("Truncated putpage record"));
goto out;
}
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_CREATE:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated create record"));
goto out;
}
if ((rc = (struct cachefs_log_create_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_MKDIR:
gettext("Truncated mkdir record"));
goto out;
}
if ((rc = (struct cachefs_log_mkdir_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_RENAME:
gettext("Truncated rename record"));
goto out;
}
if ((rc = (struct cachefs_log_rename_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_SYMLINK:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated symlink record"));
goto out;
}
if ((rc = (struct cachefs_log_symlink_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_POPULATE:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated populate record"));
goto out;
}
if ((rc = (struct cachefs_log_populate_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_CSYMLINK:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated csymlink record"));
goto out;
}
if ((rc = (struct cachefs_log_csymlink_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_FILLDIR:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated filldir record"));
goto out;
}
if ((rc = (struct cachefs_log_filldir_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_MDCREATE:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated mdcreate record"));
goto out;
}
if ((rc = (struct cachefs_log_mdcreate_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_GPFRONT:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated gpfront record"));
goto out;
}
if ((rc = (struct cachefs_log_gpfront_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_RFDIR:
gettext("Truncated rfdir record"));
goto out;
}
if ((rc = (struct cachefs_log_rfdir_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_UALLOC:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated ualloc record"));
goto out;
}
if ((rc = (struct cachefs_log_ualloc_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_CALLOC:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated calloc record"));
goto out;
}
if ((rc = (struct cachefs_log_calloc_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
case CACHEFS_LOG_NOCACHE:
(! xdr_u_longlong_t(xdrs,
gettext("Truncated nocache record"));
goto out;
}
if ((rc = (struct cachefs_log_nocache_record *)
gettext("Cannot malloc record"));
goto out;
}
break;
default:
gettext("Corrupt logfile (position %x)"),
break;
}
out:
return (rc);
}
/*
* convert a logfile record (read by stats_log_logfile_read()) to
* ascii. probably not for end-user consumption, but this should be
* the official way to do it.
*/
char *
{
struct cachefs_log_mount_record *mountp;
struct cachefs_log_umount_record *umountp;
struct cachefs_log_getpage_record *getpagep;
struct cachefs_log_readdir_record *readdirp;
struct cachefs_log_readlink_record *readlinkp;
struct cachefs_log_remove_record *removep;
struct cachefs_log_rmdir_record *rmdirp;
struct cachefs_log_truncate_record *truncatep;
struct cachefs_log_putpage_record *putpagep;
struct cachefs_log_create_record *createp;
struct cachefs_log_mkdir_record *mkdirp;
struct cachefs_log_rename_record *renamep;
struct cachefs_log_symlink_record *symlinkp;
struct cachefs_log_populate_record *populatep;
struct cachefs_log_csymlink_record *csymlinkp;
struct cachefs_log_filldir_record *filldirp;
struct cachefs_log_mdcreate_record *mdcreatep;
struct cachefs_log_gpfront_record *gpfrontp;
struct cachefs_log_rfdir_record *rfdirp;
struct cachefs_log_ualloc_record *uallocp;
struct cachefs_log_calloc_record *callocp;
struct cachefs_log_nocache_record *nocachep;
recerror);
switch (rectype) {
case CACHEFS_LOG_MOUNT:
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_UMOUNT:
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_GETPAGE:
" %-8s %llx %s %llu %ld %llu %u",
"Getpage",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_READDIR:
" %-8s %llx %s %llu %d %llx %d", "Readdir",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_READLINK:
" %-8s %llx %s %llu %d %u", "Readlink",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_REMOVE:
" %-8s %llx %s %llu %d", "Remove",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_RMDIR:
" %-8s %llx %s %llu %d", "Rmdir",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_TRUNCATE:
" %-8s %llx %s %llu %d %llu", "Truncate",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_PUTPAGE:
" %-8s %llx %s %llu %d %llu %u", "Putpage",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_CREATE:
" %-8s %llx %s %llu %d", "Create",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_MKDIR:
" %-8s %llx %s %llu %d", "Mkdir",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_RENAME:
" %-8s %llx %s %llu %d %d", "Rename",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_SYMLINK:
" %-8s %llx %s %llu %d %u", "Symlink",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_POPULATE:
" %-8s %llx %s %llu %llu %d", "Populate",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_CSYMLINK:
" %-8s %llx %s %llu %d", "Csymlink",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_FILLDIR:
" %-8s %llx %s %llu %d", "Filldir",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_MDCREATE:
" %-8s %llx %s %llu %u", "Mdcreate",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_GPFRONT:
" %-8s %llx %s %llu %d %llu %u", "Gpfront",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_RFDIR:
" %-8s %llx %s %llu %d", "Rfdir",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_UALLOC:
" %-8s %llx %s %llu %llu %u", "Ualloc",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_CALLOC:
" %-8s %llx %s %llu %llu %u", "Calloc",
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_NOCACHE:
" %-8s %llx %s %llu", "Nocache",
sizeof (st->st_asciirec));
break;
default:
"Attempt to format invalid log type=%d (position %x)"),
return (NULL);
}
return (st->st_asciirec);
}
{
struct cachefs_log_getpage_record *getpagep;
struct cachefs_log_readdir_record *readdirp;
struct cachefs_log_readlink_record *readlinkp;
struct cachefs_log_remove_record *removep;
struct cachefs_log_rmdir_record *rmdirp;
struct cachefs_log_truncate_record *truncatep;
struct cachefs_log_putpage_record *putpagep;
struct cachefs_log_create_record *createp;
struct cachefs_log_mkdir_record *mkdirp;
struct cachefs_log_rename_record *renamep;
struct cachefs_log_symlink_record *symlinkp;
struct cachefs_log_populate_record *populatep;
struct cachefs_log_csymlink_record *csymlinkp;
struct cachefs_log_filldir_record *filldirp;
struct cachefs_log_mdcreate_record *mdcreatep;
struct cachefs_log_gpfront_record *gpfrontp;
struct cachefs_log_rfdir_record *rfdirp;
struct cachefs_log_ualloc_record *uallocp;
struct cachefs_log_calloc_record *callocp;
struct cachefs_log_nocache_record *nocachep;
switch (type) {
case CACHEFS_LOG_RFDIR:
error = 0;
break;
}
if (error != 0)
return (0);
switch (type) {
case CACHEFS_LOG_GETPAGE:
break;
case CACHEFS_LOG_READDIR:
break;
case CACHEFS_LOG_READLINK:
break;
case CACHEFS_LOG_REMOVE:
break;
case CACHEFS_LOG_RMDIR:
break;
case CACHEFS_LOG_TRUNCATE:
break;
case CACHEFS_LOG_PUTPAGE:
break;
case CACHEFS_LOG_CREATE:
break;
case CACHEFS_LOG_MKDIR:
break;
case CACHEFS_LOG_RENAME:
rc = GRI_MODIFY;
break;
case CACHEFS_LOG_SYMLINK:
break;
case CACHEFS_LOG_POPULATE:
break;
case CACHEFS_LOG_CSYMLINK:
break;
case CACHEFS_LOG_FILLDIR:
break;
case CACHEFS_LOG_MDCREATE:
rc = GRI_METADATA;
break;
case CACHEFS_LOG_GPFRONT:
break;
case CACHEFS_LOG_RFDIR:
break;
case CACHEFS_LOG_UALLOC:
break;
case CACHEFS_LOG_CALLOC:
break;
case CACHEFS_LOG_NOCACHE:
break;
}
return (rc);
}
/*
* ascii formatter for fids. returns a malloc()ed string -- it's up to
* the caller to free it.
*/
static char *
{
rc = "out of memory";
return (rc);
}
void
{
int i, j;
/* shortcut if we had some sort of zero-length thing */
return;
/* `smear' the offset and length to block boundaries */
/*
* pre-largefiles: iend = off & ~(st->st_loghead.lh_maxbsize - 1);
* largefiles: make sure that we ~ all bits in the 64 bit
* version of (st->st_loghead.lh_maxbsize - 1)
*/
/*
* pre-largefiles: jend &= ~(st->st_loghead.lh_maxbsize - 1);
* largefiles: make sure that we ~ all bits in the 64 bit
* version of (st->st_loghead.lh_maxbsize - 1)
*/
/* see if our offset falls within an existing chunk */
break;
}
/* update the chunk, or make a new one */
} else if (i < C_MAX_ALLOCINFO_SLOTS) {
} else {
/* cachefs does a nocache, so we'll immitate */
/*
* XXX we're free to grow again. assume we got
* inactivated right away -- the worst case!
*/
}
/* quit for the trivial (hopefully the usual) case... */
else
return;
}
/*
* we have to see if we can consolidate any chunks. the
* chunks aren't guaranteed to be in any kind of order, so we
* do a qsort. otherwise, the consolidation would be N^2 (but
* we're probably close here).
*/
/* tag non-essential entries with offset == -1, and consolidate */
continue;
break;
}
}
/* get rid of non-essential entries (without preserving order) */
/* add up the new total size */
}
static int
stats_log_fi_comp(const void *a, const void *b)
{
return (1);
return (-1);
return (0);
}
void
{
}
struct cachefs_log_logfile_header *
{
return (&st->st_loghead);
}
void
{
void *record;
int type;
struct cachefs_log_mount_record *mountp;
struct cachefs_log_umount_record *umountp;
/*
* The maximum size of a mount_info structure is the size of
* the structure less the space already defined for char mi_path[]
* plus the maximum size of mi_path.
*
* Additional space is allocated to mi_path at runtime using
* malloc(). The size needs to be calculated in-situ as ANSI C
* will only allow 'sizeof expression' or 'sizeof (type)'.
*/
goto out;
}
switch (type) {
case CACHEFS_LOG_MOUNT:
break;
continue;
sizeof (vfsp));
continue;
break;
}
}
/* non-idempotent setup stuff */
}
/*
* idempotent setup stuff
*
* Careful string handling around mi_path
* is required as it contains two NULL
* terminated strings.
*/
gettext("Path too long in log file"));
break;
}
gettext("CacheID too long in log file"));
break;
}
break;
case CACHEFS_LOG_UMOUNT:
break;
break;
mip->mi_mounted = 0;
break;
default:
if (rflags == 0) /* shortcut */
break;
break;
}
/* account for the creation of the fscache */
/* account for the .cfs_option file */
st->st_ws_current +=
}
/*
* Add in the size-growth of the attrcache.
* len will be non-zero only for the record type
* CACHEFS_LOG_MDCREATE, and len can't be > 2GB because
* it refers to the number of entries in
* the attribute cache file.
*/
/* see if this is an `expensive' logfile */
/* subtract current frontfile size ... */
/* compute new frontfile size */
(rflags & GRI_MODIFY)) {
}
if (rflags & GRI_METADATA)
/* add back in new frontfile size */
break;
}
if (stats_inerror(st))
break;
}
out:
if (! stats_inerror(st))
}
int
{
return (st->st_ws_init);
}
{
return (st->st_ws_current);
}
{
return (st->st_ws_high);
}
int
{
return (st->st_ws_expensive);
}