fileset.c revision f2fc321be9b4df7748e8c31a5edd154b0177b139
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <math.h>
#include <libgen.h>
#include "fileset.h"
#include "filebench.h"
#include "gamma_dist.h"
/*
* File sets, of type fileset_t, are entities which contain
* information about collections of files and subdirectories in Filebench.
* The fileset, once populated, consists of a tree of fileset entries of
* type filesetentry_t which specify files and directories. The fileset
* is rooted in a directory specified by fs_path, and once the populated
* fileset has been created, has a tree of directories and files
* corresponding to the fileset's filesetentry tree.
*/
/*
* Removes the last file or directory name from a pathname.
* Basically removes characters from the end of the path by
* setting them to \0 until a forward slash '/' is
* encountered. It also removes the forward slash.
*/
static char *
trunc_dirname(char *dir)
{
while (s != dir) {
int c = *s;
*s = 0;
if (c == '/')
break;
s--;
}
return (dir);
}
/*
* Prints a list of allowed options and how to specify them.
*/
void
fileset_usage(void)
{
"entries=<number>\n");
"(Gamma * 1000)\n");
" [,sizegamma=[100-10000] (Gamma * 1000)\n");
" [,prealloc=[percent]]\n");
}
/*
* Frees up memory mapped file region of supplied size. The
* file descriptor "fd" indicates which memory mapped file.
* If successful, returns 0. Otherwise returns -1 if "size"
* is zero, or -1 times the number of times msync() failed.
*/
static int
{
int ret = 0;
}
return (ret);
}
/*
* Creates a path string from the filesetentry_t "*entry"
* and all of its parent's path names. The resulting path
* is a concatination of all the individual parent paths.
* Allocates memory for the path string and returns a
* pointer to it.
*/
char *
{
char path[MAXPATHLEN];
char pathtmp[MAXPATHLEN];
char *s;
*path = 0;
while (fsep->fse_parent) {
}
return (s);
}
/*
* Creates multiple nested directories as required by the
* supplied path. Starts at the end of the path, creating
* a list of directories to mkdir, up to the root of the
* path, then mkdirs them one at a time from the root on down.
*/
static int
{
char *p;
char *dirs[65536];
int i = 0;
goto null_str;
/*
* Fill an array of subdirectory path names until either we
* reach the root or encounter an already existing subdirectory
*/
/* CONSTCOND */
while (1) {
break;
if (strlen(p) < 3)
break;
free(p);
goto null_str;
}
(void) trunc_dirname(p);
i++;
}
/* Make the directories, from closest to root downwards. */
for (--i; i >= 0; i--) {
}
free(p);
return (0);
/* clean up */
for (--i; i >= 0; i--)
"Failed to create directory path %s: Out of memory", path);
return (-1);
}
/*
* First creates the parent directories of the file using
* fileset_mkdir(). Then Optionally sets the O_DSYNC flag
* and opens the file with open64(). It unlocks the fileset
* entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags
* as requested, and returns the file descriptor integer
* for the opened file.
*/
int
{
char path[MAXPATHLEN];
char dir[MAXPATHLEN];
char *pathtmp;
int fd;
int open_attrs = 0;
*path = 0;
(void) trunc_dirname(dir);
/* If we are going to create a file, create the parent dirs */
return (-1);
}
if (attrs & FLOW_ATTR_DSYNC) {
#ifdef sun
open_attrs |= O_DSYNC;
#else
open_attrs |= O_FSYNC;
#endif
}
"Failed to open file %s: %s",
return (-1);
}
#ifdef sun
if (attrs & FLOW_ATTR_DIRECTIO)
else
#endif
return (fd);
}
/*
* Selects a fileset entry from a fileset. If the
* FILESET_PICKDIR flag is set it will pick a directory
* entry, otherwise a file entry. The FILESET_PICKRESET
* flag will cause it to reset the free list to the
* overall list (file or directory). The FILESET_PICKUNIQUE
* flag will take an entry off of one of the free (unused)
* lists (file or directory), otherwise the entry will be
* picked off of one of the rotor lists (file or directory).
* The FILESET_PICKEXISTS will insure that only extant
* (FSE_EXISTS) state files are selected, while
* FILESET_PICKNOEXIST insures that only non extant
* (not FSE_EXISTS) state files are selected.
*/
{
while (entry) {
}
}
while (entry) {
}
}
if (flags & FILESET_PICKUNIQUE) {
if (flags & FILESET_PICKDIR) {
goto empty;
} else {
goto empty;
}
} else {
if (flags & FILESET_PICKDIR) {
} else {
}
}
goto empty;
/* Return locked entry */
/* If we ask for an existing file, go round again */
if ((flags & FILESET_PICKEXISTS) &&
}
/* If we ask for not an existing file, go round again */
if ((flags & FILESET_PICKNOEXIST) &&
}
}
return (entry);
return (NULL);
}
/*
* Given a fileset "fileset", create the associated files as
* specified in the attributes of the fileset. The fileset is
* rooted in a directory whose pathname is in fs_path. If the
* directory exists, meaning that there is already a fileset,
* and the fs_reuse attribute is false, then remove it and all
* its contained files and subdirectories. Next, the routine
* creates a root directory for the fileset. All the file type
* filesetentries are cycled through creating as needed
* their containing subdirectory trees in the filesystem and
* creating actual files for fs_preallocpercent of them. The
* created files are filled with fse_size bytes of unitialized
* data. The routine returns -1 on errors, 0 on success.
*/
static int
{
char path[MAXPATHLEN];
char *buf;
int preallocated = 0;
int reusing = 0;
return (-1);
return (-1);
}
/* XXX Add check to see if there is enough space */
/* Remove existing */
char cmd[MAXPATHLEN];
"Removed any existing fileset %s in %d seconds",
} else {
/* we are re-using */
reusing = 1;
"Re-using fileset %s on %s file system.",
}
}
char dir[MAXPATHLEN];
char *pathtmp;
int fd;
int randno;
continue;
}
*path = 0;
(void) trunc_dirname(dir);
return (-1);
}
}
/ 100);
continue;
}
preallocated++;
/* see if reusing and this file exists */
"Attempted but failed to Re-use file %s",
path);
return (-1);
}
"Re-using file %s", path);
(void) fileset_freemem(fd,
continue;
/* reuse, but too large */
"Truncating & re-using file %s", path);
(void) ftruncate64(fd,
(void) fileset_freemem(fd,
continue;
}
} else {
/* No file or not reusing, so create */
"Failed to pre-allocate file %s: %s",
return (-1);
}
}
int ret = 0;
/*
* Write FILE_ALLOC_BLOCK's worth,
* except on last write
*/
"Failed to pre-allocate file %s: %s",
return (-1);
}
}
}
"Preallocated %d of %lld of fileset %s in %d seconds",
*(fileset->fs_entries),
return (0);
}
/*
* Adds an entry to the fileset's file list. Single threaded so
* no locking needed.
*/
static void
{
} else {
}
}
/*
* Adds an entry to the fileset's directory list. Single
* threaded so no locking needed.
*/
static void
{
} else {
}
}
/*
* Obtaines a filesetentry entity for a file to be placed in a
* (sub)directory of a fileset. The size of the file may be
* specified by fs_meansize, or calculated from a gamma
* distribution of parameter fs_sizegamma and of mean size
* fs_meansize. The filesetentry entity is placed on the file
* list in the specified parent filesetentry entity, which may
* be a directory filesetentry, or the root filesetentry in the
* fileset. It is also placed on the fileset's list of all
* contained files. Returns 0 if successful or -1 if ipc memory
* for the path string cannot be allocated.
*/
static int
{
char tmpname[16];
double drand;
double gamma;
== NULL) {
"fileset_populate_file: Can't malloc filesetentry");
return (-1);
}
"fileset_populate_file: Can't alloc path string");
return (-1);
}
if (gamma > 0) {
} else {
}
fileset->fs_realfiles++;
return (0);
}
/*
* Creates a directory node in a fileset, by obtaining a
* filesetentry entity for the node and initializing it
* according to parameters of the fileset. It determines a
* directory tree depth and directory width, optionally using
* a gamma distribution. If its calculated depth is less then
* its actual depth in the directory tree, it becomes a leaf
* node and files itself with "width" number of file type
* filesetentries, otherwise it files itself with "width"
* number of directory type filesetentries, using recursive
* calls to fileset_populate_subdir. The end result of the
* initial call to this routine is a tree of directories of
* random width and varying depth with sufficient leaf
* directories to contain all required files.
* Returns 0 on success. Returns -1 if ipc path string memory
* cannot be allocated and returns an error code (currently
* also -1) from calls to fileset_populate_file or recursive
* calls to fileset_populate_subdir.
*/
static int
{
int isleaf = 0;
char tmpname[16];
int i;
depth += 1;
/* Create dir node */
== NULL) {
"fileset_populate_subdir: Can't malloc filesetentry");
return (-1);
}
"fileset_populate_subdir: Can't alloc path string");
return (-1);
}
if (gamma > 0) {
} else {
}
if (gamma > 0) {
} else {
}
if (randepth == 0)
randepth = 1;
if (ranwidth == 0)
ranwidth = 1;
isleaf = 1;
/*
* Create directory of random width according to distribution, or
* if root directory, continue until #files required
*/
for (i = 1;
int ret = 0;
else
if (ret != 0)
return (ret);
}
return (0);
}
/*
* Populates a fileset with files and subdirectory entries. Uses
* the supplied fs_dirwidth and fs_entries (number of files) to
* calculate the required fs_meandepth (of subdirectories) and
* initialize the fs_meanwidth and fs_meansize variables. Then
* calls fileset_populate_subdir() to do the recursive
* subdirectory entry creation and leaf file entry creation. All
* of the above is skipped if the fileset has already been
* populated. Returns 0 on success, or an error code from the
* call to fileset_populate_subdir if that call fails.
*/
static int
{
int nfiles;
int ret;
/* Skip if already populated */
goto exists;
/*
* Input params are:
* # of files
* ave # of files per dir
* max size of dir
* # ave size of file
* max size of file
*/
return (ret);
"avg dir = %.1lf, avg depth = %.1lf, mbytes=%lld",
*(fileset->fs_entries),
return (0);
}
/*
* Allocates a fileset instance, initializes fs_dirgamma and
* fs_sizegamma default values, and sets the fileset name to the
* supplied name string. Puts the allocated fileset on the
* master fileset list and returns a pointer to it.
*/
fileset_define(char *name)
{
return (NULL);
"fileset_define: Can't malloc fileset");
return (NULL);
}
/* Add fileset to global list */
} else {
}
return (fileset);
}
/*
* If supplied with a pointer to a fileset and the fileset's
* fs_prealloc flag is set, calls fileset_populate() to populate
* the fileset with filesetentries, then calls fileset_create()
* to make actual directories and files for the filesetentries.
* Otherwise, it applies fileset_populate() and fileset_create()
* to all the filesets on the master fileset list. It always
* returns zero (0) if one fileset is populated / created,
* otherwise it returns the sum of returned values from
* fileset_create() and fileset_populate(), which
* will be a negative one (-1) times the number of
* fileset_create() calls which failed.
*/
int
{
int ret = 0;
return (ret);
return (fileset_create(fileset));
}
while (list) {
}
return (ret);
}
/*
* Searches through the master fileset list for the named fileset.
* If found, returns pointer to same, otherwise returns NULL.
*/
fileset_find(char *name)
{
while (fileset) {
return (fileset);
}
}
return (NULL);
}