[sfs commit] r33 - trunk/src

0 views
Skip to first unread message

codesite...@google.com

unread,
Nov 25, 2008, 11:23:43 PM11/25/08
to sfs-dev...@googlegroups.com
Author: matt.burkhart
Date: Tue Nov 25 20:23:08 2008
New Revision: 33

Modified:
trunk/src/CMakeLists.txt
trunk/src/logger.cc
trunk/src/logger.h
trunk/src/main.cc
trunk/src/path_utils.cc
trunk/src/path_utils.h
trunk/src/source.cc
trunk/src/source.h
trunk/src/switchfs.cc
trunk/src/switchfs.h

Log:
Balancing functionality.

Modified: trunk/src/CMakeLists.txt
==============================================================================
--- trunk/src/CMakeLists.txt (original)
+++ trunk/src/CMakeLists.txt Tue Nov 25 20:23:08 2008
@@ -1,3 +1,5 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
PROJECT(SFS)

ADD_EXECUTABLE(sfs

Modified: trunk/src/logger.cc
==============================================================================
--- trunk/src/logger.cc (original)
+++ trunk/src/logger.cc Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#include <assert.h>
#include <stdarg.h>
@@ -29,8 +28,8 @@
}

void DiskLogger::Log(const char message[], ...) {
- // The lock is required to include the calls to time and ctime since
- // they are not necessarily thread-safe.
+ // The lock is required to include the calls to time and ctime since
they are
+ // not necessarily thread-safe.
pthread_mutex_lock(&disk_log_lock_);

time_t timestamp = time(NULL);

Modified: trunk/src/logger.h
==============================================================================
--- trunk/src/logger.h (original)
+++ trunk/src/logger.h Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#ifndef LOGGER_H__
#define LOGGER_H__
@@ -20,8 +19,8 @@
public:
virtual ~Logger() {}

- // Loggers need only implement the Log method, but they must ensure
- // that the implementation is thread-safe.
+ // Loggers need only implement the Log method, but they must ensure that
the
+ // implementation is thread-safe.
virtual void Log(const char message[], ...) = 0;
};


Modified: trunk/src/main.cc
==============================================================================
--- trunk/src/main.cc (original)
+++ trunk/src/main.cc Tue Nov 25 20:23:08 2008
@@ -1,17 +1,17 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#include <assert.h>
#include <fuse.h>
+#include <stdlib.h>
#include <fstream>

#include "logger.h"
@@ -100,23 +100,25 @@
} // namespace switchfs

int main(int argc, char* argv[]) {
- // Parse the command line arguments using the FUSE-provided
- // mechanism. This also permits the use of SwitchFS within fstab.
+ // Parse the command line arguments using the FUSE-provided mechanism.
This
+ // also permits the use of SwitchFS within fstab.
struct SwitchFSOptions {
char* config_path;
int check;
+ int balance;
} switchfs_options = {NULL};
fuse_opt switchfs_command_line[] = {
{"config=%s", offsetof(SwitchFSOptions, config_path), 0},
- {"-check", offsetof(SwitchFSOptions, check), 1}, {NULL}
+ {"-check", offsetof(SwitchFSOptions, check), 1},
+ {"-balance", offsetof(SwitchFSOptions, balance), 1}, {NULL}
};
fuse_args args = FUSE_ARGS_INIT(argc, argv);
assert(!fuse_opt_parse(&args, &switchfs_options, switchfs_command_line,
NULL));
assert(switchfs_options.config_path != NULL);

- // Parse the configuration file in order to initialize the SwitchFS
- // instance. The configuration file is a line and/or space delimited
- // set of source directories.
+ // Parse the configuration file in order to initialize the SwitchFS
instance.
+ // The configuration file is a line and/or space delimited set of source
+ // directories.
LOG("Mounting SwitchFS. config_file: %s", switchfs_options.config_path);
{
std::ifstream config_file(switchfs_options.config_path);
@@ -128,9 +130,13 @@
}
}

- if (switchfs_options.check) {
+ if (switchfs_options.check || switchfs_options.balance) {
LOG("Checking filesystem directory structure...");
- g_switchfs.Check("/");
+ int error = g_switchfs.Check("/", switchfs_options.balance);
+ if (error) {
+ LOG("Check failed with error, %d.", error);
+ exit(error);
+ }
LOG("Check complete.");
}


Modified: trunk/src/path_utils.cc
==============================================================================
--- trunk/src/path_utils.cc (original)
+++ trunk/src/path_utils.cc Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#include "path_utils.h"


Modified: trunk/src/path_utils.h
==============================================================================
--- trunk/src/path_utils.h (original)
+++ trunk/src/path_utils.h Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#ifndef PATH_UTILS_H__
#define PATH_UTILS_H__

Modified: trunk/src/source.cc
==============================================================================
--- trunk/src/source.cc (original)
+++ trunk/src/source.cc Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#include <assert.h>
#include <sys/statvfs.h>
@@ -23,9 +22,9 @@
}

unsigned long long Source::GetFreeSpace() const {
- // A better implementation would cache this value. The cache can be
- // dirtied given a notification from the kernel that the underlying
- // file system has been modified.
+ // A better implementation would cache this value. The cache can be
dirtied
+ // given a notification from the kernel that the underlying file system
has
+ // been modified.
struct statvfs drive_buffer;
assert(!statvfs(location_.c_str(), &drive_buffer));

@@ -37,8 +36,8 @@
}

bool Source::operator<(const Source& operand) const {
- // This implementation gives a higher file allocation priority to
- // those sources which have more free space. See allocateSources.
+ // This implementation gives a higher file allocation priority to those
+ // sources which have more free space. See allocateSources.
return GetFreeSpace() > operand.GetFreeSpace();
}

@@ -47,7 +46,7 @@
}

Source::operator const char*() const {
- // This pointer won't stick around for long... it might be best in
- // the long run to remove this function.
+ // This pointer won't stick around for long... it might be best in the
long
+ // run to remove this function.
return location_.c_str();
}

Modified: trunk/src/source.h
==============================================================================
--- trunk/src/source.h (original)
+++ trunk/src/source.h Tue Nov 25 20:23:08 2008
@@ -1,35 +1,33 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#ifndef SOURCE_H
#define SOURCE_H

#include <string>

-// A Source is a location from the root file system that files can be
-// stored.
+// A Source is a location from the root file system that files can be
stored.
class Source {
public:
Source(const std::string& location);

unsigned long long GetFreeSpace() const;

- // The equals operator acts on the exact source location, regardless
- // of the physical device this location resides on.
+ // The equals operator acts on the exact source location, regardless of
the
+ // physical device this location resides on.
bool operator==(const Source& operand) const;

- // The less than operator dictates allocation priority. A source
- // which has the property of being "less than" another source will
- // have files allocated to it first.
+ // The less than operator dictates allocation priority. A source which
has the
+ // property of being "less than" another source will have files
allocated to
+ // it first.
bool operator<(const Source& operand) const;

// The location of this source as a string type.
@@ -38,6 +36,12 @@

private:
std::string location_;
+};
+
+struct SourceComparitor {
+ bool operator ()(const Source* source_a, const Source* source_b) const {
+ return *source_a < *source_b;
+ }
};

#endif // SOURCE_H

Modified: trunk/src/switchfs.cc
==============================================================================
--- trunk/src/switchfs.cc (original)
+++ trunk/src/switchfs.cc Tue Nov 25 20:23:08 2008
@@ -1,24 +1,26 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <sys/wait.h>
#include <unistd.h>
+#include <algorithm>
#include <fstream>
#include <set>
#include <stack>
@@ -54,15 +56,17 @@
pthread_rwlock_unlock(&sources_lock_);
}

-int SwitchFS::Check(const string& virtual_path) {
+int SwitchFS::Check(const string& virtual_path, bool balance) {
set<string> unique_files;
+ set<string> unique_directories;
+
source_map(source) {
string physical_path = Join(*source, virtual_path);
DIR* dp = ::opendir(physical_path.c_str());
if (!dp) {
- // Magic number 493 is taken from various SwitchFS logging.
- // Oddly, number does not match stat --format=%f foo which
- // appears to yield 0x41ed. Investigation needed.
+ // Magic number 493 is taken from various SwitchFS logging. Oddly,
number
+ // does not match stat --format=%f foo which appears to yield 0x41ed.
+ // Investigation needed.
LOG("DIR not found. Creating: %s", physical_path.c_str());
if (::mkdir(physical_path.c_str(), 493) == -1) {
LOG("Error creating dir: %s (%d)", strerror(errno), errno);
@@ -71,37 +75,101 @@
continue;
}

- // Track all unique file types that are DT_DIR (directories).
+ // Track all unique file types that are DT_DIR (directories) for
recursion
+ // and DT_REG (regular files) for balancing.
struct dirent *de;
while ((de = ::readdir(dp))) {
string name = de->d_name;
// Only insert unique dir names. Ignore "." and ".." dirs.
if (de->d_type == DT_DIR && name != "." && name != "..") {
+ unique_directories.insert(de->d_name);
+ } else if (balance && de->d_type == DT_REG) {
unique_files.insert(de->d_name);
}
}
::closedir(dp);
}

- for (set<string>::const_iterator iter = unique_files.begin();
- iter != unique_files.end(); ++iter) {
- Check(Join(virtual_path, *iter));
+ for (set<string>::const_iterator file = unique_files.begin();
+ file != unique_files.end(); ++file) {
+ int error = BalanceFile(Join(virtual_path, *file));
+ if (error) return error;
+ }
+ for (set<string>::const_iterator directory = unique_directories.begin();
+ directory != unique_directories.end(); ++directory) {
+ int error = Check(Join(virtual_path, *directory), balance);
+ if (error) return error;
+ }
+ return 0;
+}
+
+int SwitchFS::BalanceFile(const string& virtual_path) {
+ // The balance threshold determines the minimum amount by which the free
space
+ // must differ between current and desired sources for a balance move to
take
+ // place.
+ const unsigned long long kBalanceThreshold = 1024 * 1024 * 1024; // 1
GB.
+
+ // Note that the call to AllocateSources(...) invalidates the Source
pointer
+ // addresses so it must be called before we get the list of current file
+ // sources if we want to deal with the vector of pointers instead of
copies of
+ // instances.
+ size_t replication_count = GetFileSources(virtual_path).size();
+ vector<Source*> optimal_sources = AllocateSources(replication_count);
+ vector<Source*> current_sources = GetFileSources(virtual_path);
+
+ // Given the list of current file sources and the optimal file sources,
+ // determine which of the optimal sources to move to and which current
sources
+ // to move from based off of which has the most available free space.
+ std::sort(current_sources.begin(), current_sources.end());
+ std::sort(optimal_sources.begin(), optimal_sources.end());
+ vector<Source*> target_sources(current_sources.size());
+ vector<Source*>::iterator target_sources_end =
+ std::set_difference(optimal_sources.begin(), optimal_sources.end(),
+ current_sources.begin(), current_sources.end(),
+ target_sources.begin());
+
+ std::sort(current_sources.begin(), current_sources.end(),
SourceComparitor());
+ std::sort(target_sources.begin(), target_sources_end,
SourceComparitor());
+
+ // For each target source *starting with the least allocated* move a file
+ // replica from the *most over-allocated* source. Stop if we have moved
to all
+ // targets or even the most over-allocated source is in a similar
(enough as
+ // defined by kBalanceThreshold) situation.
+ for (vector<Source*>::const_iterator target_source =
target_sources.begin();
+ target_source != target_sources_end; ++target_source) {
+ Source* current_source = current_sources.back();
+ if ((*target_source)->GetFreeSpace() - current_source->GetFreeSpace() <
+ kBalanceThreshold) {
+ break;
+ }
+
+ string old_physical_path = Join(*current_source, virtual_path.c_str());
+ string new_physical_path = Join(**target_source, virtual_path.c_str());
+ LOG("Moving \"%s\" to \"%s\".",
+ old_physical_path.c_str(), new_physical_path.c_str());
+ const char* const args [] = {
+ "mv", "--", old_physical_path.c_str(), new_physical_path.c_str(),
NULL };
+ int error = ExecCommand("mv", args);
+ if (error) {
+ return error; // Move error.
+ }
+ current_sources.pop_back();
}
return 0;
}

vector<Source*> SwitchFS::AllocateSources(size_t count) {
- // This method decides on which sources we should place new
- // files. We choose "count" sources. count must be less or equal to
- // the number of sources we know of and greater than 0.
+ // This method decides on which sources we should place new files. We
choose
+ // "count" sources. count must be less or equal to the number of sources
we
+ // know of and greater than 0.
pthread_rwlock_wrlock(&sources_lock_);
assert(count <= sources_.size());
assert(count > 0);

- // Skip any sorting computation if we do not have any choice where
- // to replicate since we have to replicated on all sources. This
- // implementation uses the operators defined in the source class to
- // dictate allocation ordering.
+ // Skip any sorting computation if we do not have any choice where to
+ // replicate since we have to replicated on all sources. This
implementation
+ // uses the operators defined in the source class to dictate allocation
+ // ordering.
if (count != sources_.size()) {
sort(sources_.begin(), sources_.end());
}
@@ -139,8 +207,8 @@
// File exists.
if (sources.size() > 0) return *sources.begin();

- // File does not yet exist. We can return an hypothetical path for
- // methods which expect a result regardless of file existence.
+ // File does not yet exist. We can return an hypothetical path for
methods
+ // which expect a result regardless of file existence.
assert(sources_.size() >= 1);
return &sources_[0];
}
@@ -161,8 +229,7 @@
}

size_t SwitchFS::GetDesiredReplication(const string& virtual_path) {
- // If this is a replication directive, we need to put it on all
- // drives.
+ // If this is a replication directive, we need to put it on all drives.
if (Basename(virtual_path) == GetDirectiveFilename()) {
pthread_rwlock_rdlock(&sources_lock_);
size_t source_count = sources_.size();
@@ -170,9 +237,9 @@
return source_count;
}

- // If not, find the corresponding replication directive file. We
- // assume that the replication directions are stored on each source
- // so it doesn't matter which source path we crawl.
+ // If not, find the corresponding replication directive file. We assume
that
+ // the replication directions are stored on each source so it doesn't
matter
+ // which source path we crawl.
size_t desired_replication = GetDefaultReplicationLevel();
pthread_rwlock_rdlock(&sources_lock_);
string source_path = *sources_.begin();
@@ -180,9 +247,9 @@
path.size() >= source_path.size(); path = StripBasename(path)) {
string directive_path = Join(path, GetDirectiveFilename());

- // Check if the file exists. If so, open if an read out the
- // replication level. If the file is malformed, all we can do is
- // print out a warning and ignore the file.
+ // Check if the file exists. If so, open if an read out the replication
+ // level. If the file is malformed, all we can do is print out a
warning and
+ // ignore the file.
DEBUG("Looking for directive file at: " << directive_path << endl);
if (::access(directive_path.c_str(), F_OK) == 0) {
ifstream directive_file(directive_path.c_str());
@@ -200,6 +267,18 @@
return desired_replication;
}

+int SwitchFS::ExecCommand(const char* cmd, const char* const* args) {
+ pid_t pid = fork();
+ if (pid == 0) { // Child process.
+ execvp(cmd, (char* const*)args);
+ exit(1);
+ } else { // Parent process.
+ int status;
+ waitpid(pid, &status, 0);
+ return WIFEXITED(status) && !WEXITSTATUS(status) ? 0 : -1;
+ }
+}
+
Logger& SwitchFS::logger() {
assert(logger_ != NULL);
return *logger_;
@@ -221,27 +300,25 @@
}

unsigned long long SwitchFS::GetBytesReserved() {
- return 1024 * 1024; // ~1 MB
+ return 1024 * 1024; // 1 MB
}

////
// FUSE Interface

int SwitchFS::statfs(const char* path, struct statvfs* buffer) {
- // We need to return aggregate data for this file system. In most
- // cases, this is a product of the source file systems we are
- // mapping to. It would be nice to be able to cache this
- // information.
+ // We need to return aggregate data for this file system. In most cases,
this
+ // is a product of the source file systems we are mapping to. It would
be nice
+ // to be able to cache this information.

buffer->f_bsize = 0;
pthread_rwlock_rdlock(&sources_lock_);
source_map(source) {
- // Start off using the data from our first source with our
- // specific flags. We can then start adding statistics on top of
- // that.
+ // Start off using the data from our first source with our specific
flags.
+ // We can then start adding statistics on top of that.
if (buffer->f_bsize == 0) {
::statvfs(*source, buffer);
- buffer->f_fsid = 0x1337; // Is this already used?
+ buffer->f_fsid = 0x1337; // Is this already used?
}

// Aggregate statistics from further sources.
@@ -249,8 +326,8 @@
struct statvfs source_buffer;
::statvfs(*source, &source_buffer);

- // TODO: How to incorporate different block sizes and fragment
- // sizes: buffer->f_bsize, buffer->f_frsize.
+ // TODO: How to incorporate different block sizes and fragment sizes:
+ // buffer->f_bsize, buffer->f_frsize.
buffer->f_blocks += source_buffer.f_blocks;
buffer->f_bfree += source_buffer.f_bfree;
buffer->f_bavail += source_buffer.f_bavail;
@@ -318,8 +395,8 @@
source_map(source) {
string file_path = Join(*source, path);

- // Create the new directory. Note: If some of these fail while
- // others succeed, we will be left in a bad state.
+ // Create the new directory. Note: If some of these fail while others
+ // succeed, we will be left in a bad state.
if (::mkdir(file_path.c_str(), mode)) {
ret = -errno;
}
@@ -331,11 +408,10 @@
int SwitchFS::readdir(const char* path, void* buf,
fuse_fill_dir_t filler, off_t offset,
struct fuse_file_info* fi) {
- // For each source disk source we have, we keep track of unique
- // files. Note: This implementation could be made fast with a merge
- // sort approach. That would also make sure our output is in the
- // right order which it is not now. Or does another fs layer do a
- // sorting?
+ // For each source disk source we have, we keep track of unique files.
Note:
+ // This implementation could be made fast with a merge sort approach.
That
+ // would also make sure our output is in the right order which it is not
now.
+ // Or does another fs layer do a sorting?
set<string> unique_files;

pthread_rwlock_rdlock(&sources_lock_);
@@ -351,9 +427,8 @@

struct dirent *de;
while ((de = ::readdir(dp))) {
- if (unique_files.find(de->d_name) != unique_files.end()) {
- continue;
- }
+ if (unique_files.find(de->d_name) != unique_files.end())
+ continue;
unique_files.insert(de->d_name);
if (filler(buf, de->d_name, NULL, 0))
break;
@@ -365,10 +440,10 @@
}

int SwitchFS::mknod(const char* path, mode_t mode, dev_t dev) {
- // File creation is where we decide which sources (devices) will
- // house this file. The number of sources we store this file on is
- // equal to the desired replication level of the file. The sources
- // we store this file on is decided by the balancing algorithm.
+ // File creation is where we decide which sources (devices) will house
this
+ // file. The number of sources we store this file on is equal to the
desired
+ // replication level of the file. The sources we store this file on is
decided
+ // by the balancing algorithm.
int replication_level = GetDesiredReplication(path);
vector<Source*> new_sources = AllocateSources(replication_level);

@@ -379,8 +454,8 @@
source != new_sources.end(); ++source) {
string file_path = Join(**source, path);

- // XXX Note that if some of these fail while others succeed, we
- // will be left in a bad state.
+ // XXX Note that if some of these fail while others succeed, we will
be left
+ // in a bad state.
if (::mknod(file_path.c_str(), mode, dev)) {
return -errno;
}
@@ -389,8 +464,8 @@
}

int SwitchFS::unlink(const char* path) {
- // We want lazy deletion. This just calls rename with our deleted
- // file prefix in order to move the file(s) there.
+ // We want lazy deletion. This just calls rename with our deleted file
prefix
+ // in order to move the file(s) there.
string new_path = Join(SwitchFS::GetDeletedPrefix(), path);

// We need to ensure the base directories exist.
@@ -419,8 +494,8 @@
pthread_rwlock_rdlock(&sources_lock_);
source_map(source) {
string full_path = Join(*source, virtual_path);
- // XXX Note that if some of these fail while others succeed, we
- // will be left in a bad state.
+ // XXX Note that if some of these fail while others succeed, we will
be left
+ // in a bad state.
if (::rmdir(full_path.c_str())) {
pthread_rwlock_unlock(&sources_lock_);
return -errno;
@@ -438,9 +513,9 @@
return -ENOENT;
}

- // We have to call remove on any existing file we want to overwrite
- // since they may be on different drives and the low level fs
- // doesn't know about this and won't do this for us.
+ // We have to call remove on any existing file we want to overwrite
since they
+ // may be on different drives and the low level fs doesn't know about
this and
+ // won't do this for us.
vector<Source*> targets = GetFileSources(new_path);
if (targets.size() > 0) {
DEBUG("rename: backing-up move target: " << new_path << endl);
@@ -463,18 +538,17 @@
}

int SwitchFS::open(const char *path, struct fuse_file_info *fi) {
- // If we are opening a file for writing, we open and obtain a file
- // descriptor for all real paths associated with this virtual
- // path. This is not necessary for reading, but we do it anyway now
- // for simplicity. The fh member of the fuse_file_info can be used
- // to store a set of open file descriptors.
+ // If we are opening a file for writing, we open and obtain a file
descriptor
+ // for all real paths associated with this virtual path. This is not
necessary
+ // for reading, but we do it anyway now for simplicity. The fh member of
the
+ // fuse_file_info can be used to store a set of open file descriptors.

set<int>* file_descriptors = new set<int>;
vector<string> paths = GetFilePaths(path);
for (vector<string>::const_iterator physical_path = paths.begin();
physical_path != paths.end(); ++physical_path) {
- // XXX Note that if some of these fail while others succeed, we
- // will be left in a bad state.
+ // XXX Note that if some of these fail while others succeed, we will
be left
+ // in a bad state.
int file_descriptor = ::open(physical_path->c_str(), fi->flags);
if (file_descriptor == -1) {
return -errno;
@@ -488,9 +562,8 @@
}

int SwitchFS::release(const char* path, struct fuse_file_info* fi) {
- // There are no more references to these file descriptors
- // anymore. We can now safely close the files and release the
- // associated memory.
+ // There are no more references to these file descriptors anymore. We
can now
+ // safely close the files and release the associated memory.
set<int>* file_descriptors = reinterpret_cast<set<int>*>(fi->fh);
assert(file_descriptors);
assert(file_descriptors->size() > 0);
@@ -510,10 +583,10 @@

int SwitchFS::read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi) {
- // The fh member variable of the fuse_file_info stores a pointer to
- // a set of file descriptors associated with this virtual path. For
- // reading operations, we can read from either of them. Here we
- // extract the first from the set and read from it.
+ // The fh member variable of the fuse_file_info stores a pointer to a
set of
+ // file descriptors associated with this virtual path. For reading
operations,
+ // we can read from either of them. Here we extract the first from the
set and
+ // read from it.
set<int>* file_descriptors = reinterpret_cast<set<int>*>(fi->fh);
assert(file_descriptors);
assert(file_descriptors->size() > 0);
@@ -528,11 +601,10 @@

int SwitchFS::write(const char* path, const char* buffer, size_t size,
off_t offset, struct fuse_file_info* fi) {
- // The fh member variable of the fuse_file_info stores a pointer to
- // a set of file descriptors associated with this virtual path. For
- // writing operations, we must write to each one of these. Further
- // optimization of this method could include asynchronous writes
- // here.
+ // The fh member variable of the fuse_file_info stores a pointer to a
set of
+ // file descriptors associated with this virtual path. For writing
operations,
+ // we must write to each one of these. Further optimization of this
method
+ // could include asynchronous writes here.
set<int>* file_descriptors = reinterpret_cast<set<int>*>(fi->fh);
assert(file_descriptors);
assert(file_descriptors->size() > 0);
@@ -540,9 +612,8 @@
for (set<int>::const_iterator file_descriptor =
file_descriptors->begin();
file_descriptor != file_descriptors->end(); ++file_descriptor) {

- // SwitchFS always reserves space per source for creation of
- // directories. TODO: Determine if the overhead of the call to
- // fstatvfs is measurable.
+ // SwitchFS always reserves space per source for creation of
directories.
+ // TODO: Determine if the overhead of the call to fstatvfs is
measurable.
struct statvfs source_stat;
if (fstatvfs(*file_descriptor, &source_stat) == -1) {
return -errno;
@@ -554,15 +625,15 @@
return -ENOSPC;
}

- // Note that if some of these fail while others succeed, we will
- // be left in a bad state.
+ // Note that if some of these fail while others succeed, we will be
left in
+ // a bad state.
int result = pwrite(*file_descriptor, buffer, size, offset);
if (result == -1) {
return -errno;
}
- // Fuse requires that the amount of bytes specified to this call
- // are written. Since the documentation of pwrite does not
- // necessarily guarantee this, we must check here.
+ // Fuse requires that the amount of bytes specified to this call are
+ // written. Since the documentation of pwrite does not necessarily
guarantee
+ // this, we must check here.
assert(result == int(size));
}
return size;

Modified: trunk/src/switchfs.h
==============================================================================
--- trunk/src/switchfs.h (original)
+++ trunk/src/switchfs.h Tue Nov 25 20:23:08 2008
@@ -1,14 +1,13 @@
// Copyright 2008 and onwards Matt Burkhart, Mike Chan.
//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; version 2 of the
-// License.
+// This program is free software; you can redistribute it and/or modify it
under
+// the terms of the GNU General Public License as published by the Free
Software
+// Foundation; version 2 of the License.
//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
+// This program is distributed in the hope that it will be useful, but
WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.

#ifndef SWITCHFS_H__
#define SWITCHFS_H__
@@ -21,33 +20,31 @@
#include "logger.h"
#include "source.h"

-// SwitchFS exposes an interface to a SwitchFS file system. All
-// methods are thread-safe except where explicitly noted.
+// SwitchFS exposes an interface to a SwitchFS file system. All methods are
+// thread-safe except where explicitly noted.
class SwitchFS {
public:
// Initialize a SwitchFS instance. Logger must not be NULL.
SwitchFS(Logger* logger);

- // Add a data path to the file system. SwitchFS currently assumes
- // all sources have been added before any of the file io methods are
- // called.
+ // Add a data path to the file system. SwitchFS currently assumes all
sources
+ // have been added before any of the file io methods are called.
void AddSource(const std::string& location);

- // Ensure that all sources have the same directory structure. This
- // method is not thread-safe and must be invoked before the
- // filesystem is accessed. This is invoked via -check command line
- // flag. Recursive function that checks dir structure of sources
- // using DFS traversal.
+ // Ensure that all sources have the same directory structure. This
method is
+ // not thread-safe and must be invoked before the filesystem is
accessed. This
+ // is invoked via -check command line flag. Recursive function that
checks dir
+ // structure of sources using DFS traversal.
// Args:
// virtual_path - The SwitchFS path excluding the mount point. ie:
// /mnt/sfs/<virtual_path>.
// Returns:
// Returns 0 on success, error code from errno otherwise.
- int Check(const std::string& virtual_path);
+ int Check(const std::string& virtual_path, bool balance);

- // File system interface methods designed to interface with FUSE.
- // For more documentation, equivalents are all defined in fuse.h,
- // alternatively see "man 2 foo"
+ // File system interface methods designed to interface with FUSE. For
more
+ // documentation, equivalents are all defined in fuse.h, alternatively
see
+ // "man 2 foo"

// Get file system statistics.
int statfs(const char* path, struct statvfs* buffer);
@@ -109,8 +106,14 @@
private:
Logger& logger();

- // Choose count number of source locations to place the "next" new
- // file(s).
+ // For a specified file, redistribute its replicas across drives as if
the
+ // file was newly created according to the file allocation policy. This
has
+ // the effect of redistributing load across sources evenly. Note that
this
+ // does not check or enforce desired file replication levels. However
this
+ // could be extended to do so in the future.
+ int BalanceFile(const std::string& virtual_path);
+
+ // Choose count number of source locations to place the "next" new
file(s).
std::vector<Source*> AllocateSources(size_t count);

// Return a vector of all of the sources this file is stored on.
@@ -125,28 +128,32 @@
// Return one of the file's real, physical path(s).
std::string GetFilePath(const std::string& virtual_path);

- // Return the desired amount of replication (number of copies) for
- // the specified file. Note that a file must be specified here, not
- // a directory.
+ // Return the desired amount of replication (number of copies) for the
+ // specified file. Note that a file must be specified here, not a
directory.
size_t GetDesiredReplication(const std::string& virtual_path);

- // The directive filename specifies the name of the files in which
- // the user may specify the replication amounts for each directory
- // tree. For example, ".backup". If no such file is found,
- // GetDefaultReplicationLevel() is used.
+ // The directive filename specifies the name of the files in which the
user
+ // may specify the replication amounts for each directory tree. For
example,
+ // ".backup". If no such file is found, GetDefaultReplicationLevel() is
used.
static std::string GetDirectiveFilename();

- // Return the default number of file copies to keep. The minimum
- // valid result value is 1.
+ // Return the default number of file copies to keep. The minimum valid
result
+ // value is 1.
static size_t GetDefaultReplicationLevel();

- // The deleted prefix is the directory to place all files and
- // directories which have been deleted.
+ // The deleted prefix is the directory to place all files and directories
+ // which have been deleted.
static std::string GetDeletedPrefix();

- // Amount of space to reserve on each source location. Units in
- // bytes.
+ // Amount of space to reserve on each source location. Units in bytes.
static unsigned long long GetBytesReserved();
+
+ // Blocking call to execute the specified command with the specified
+ // arguments. The command may be an absolute path or a file name in
which case
+ // the PATH environment variables are searched for the file. If the
command
+ // was executed and then exited with a successful status, 0 is returned.
+ // Otherwise, -1 indicates and error.
+ static int ExecCommand(const char* cmd, const char* const* args);

std::vector<Source> sources_;
pthread_rwlock_t sources_lock_;

Reply all
Reply to author
Forward
0 new messages