Browse Source

add {,ep,msp}pfrd policies

Percentage Free Random Distribution

Chooses a random branch based on the available space percentage free.

IE: if branch A has 1G free and branch B has 2G then B should be chosen
twice as often.
pull/815/head
Antonio SJ Musumeci 4 years ago
parent
commit
046844083f
  1. 23
      README.md
  2. 23
      man/mergerfs.1
  3. 18
      src/policy.cpp
  4. 9
      src/policy.hpp
  5. 273
      src/policy_eppfrd.cpp
  6. 172
      src/policy_msppfrd.cpp
  7. 140
      src/policy_pfrd.cpp
  8. 55
      src/rnd.cpp
  9. 33
      src/rnd.hpp

23
README.md

@ -1,6 +1,6 @@
% mergerfs(1) mergerfs user manual % mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link> % Antonio SJ Musumeci <trapexit@spawn.link>
% 2020-08-07
% 2020-08-20
# NAME # NAME
@ -306,22 +306,25 @@ Because of the nature of the behavior the policies act diffierently depending on
| Policy | Description | | Policy | Description |
|------------------|------------------------------------------------------------| |------------------|------------------------------------------------------------|
| all | Search: same as **epall**. Action: same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| epall (existing path, all) | Search: same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
| all | Search: Same as **epall**. Action: Same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| epall (existing path, all) | Search: Same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
| epff (existing path, first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found where the relative path exists. | | epff (existing path, first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found where the relative path exists. |
| eplfs (existing path, least free space) | Of all the branches on which the relative path exists choose the drive with the least free space. | | eplfs (existing path, least free space) | Of all the branches on which the relative path exists choose the drive with the least free space. |
| eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the drive with the least used space. | | eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the drive with the least used space. |
| epmfs (existing path, most free space) | Of all the branches on which the relative path exists choose the drive with the most free space. | | epmfs (existing path, most free space) | Of all the branches on which the relative path exists choose the drive with the most free space. |
| eppfrd (existing path, percentage free random distribution) | Like **pfrd** but limited to existing paths. |
| eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. | | eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. |
| erofs | Exclusively return **-1** with **errno** set to **EROFS** (read-only filesystem). | | erofs | Exclusively return **-1** with **errno** set to **EROFS** (read-only filesystem). |
| ff (first found) | Search: same as **epff**. Action: same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: Pick the drive with the least available free space. |
| lus (least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: Pick the drive with the least used space. |
| mfs (most free space) | Search: same as **epmfs**. Action: same as **epmfs**. Create: Pick the drive with the most available free space. |
| msplfs (most shared path, least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. |
| msplus (most shared path, least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. |
| mspmfs (most shared path, most free space) | Search: same as **epmfss**. Action: same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. |
| ff (first found) | Search: Same as **epff**. Action: Same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: Pick the drive with the least available free space. |
| lus (least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: Pick the drive with the least used space. |
| mfs (most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: Pick the drive with the most available free space. |
| msplfs (most shared path, least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. |
| msplus (most shared path, least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. |
| mspmfs (most shared path, most free space) | Search: Same as **epmfss**. Action: Same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. |
| msppfrd (most shared path, percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Like **eppfrd** but will walk back the path if it fails to find a branch at that level. |
| newest | Pick the file / directory with the largest mtime. | | newest | Pick the file / directory with the largest mtime. |
| pfrd (percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Chooses a branch at random with the likelihood of selection based on a branch's available space relative to the total. |
| rand (random) | Calls **all** and then randomizes. Returns 1. | | rand (random) | Calls **all** and then randomizes. Returns 1. |
**NOTE:** If you are using an underlying filesystem that reserves blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the reservation by using `f_bavail` (number of free blocks for unprivileged users) rather than `f_bfree` (number of free blocks) in policy calculations. **df** does NOT use `f_bavail`, it uses `f_bfree`, so direct comparisons between **df** output and mergerfs' policies is not appropriate. **NOTE:** If you are using an underlying filesystem that reserves blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the reservation by using `f_bavail` (number of free blocks for unprivileged users) rather than `f_bfree` (number of free blocks) in policy calculations. **df** does NOT use `f_bavail`, it uses `f_bfree`, so direct comparisons between **df** output and mergerfs' policies is not appropriate.

23
man/mergerfs.1

@ -1,7 +1,7 @@
.\"t .\"t
.\" Automatically generated by Pandoc 1.19.2.4 .\" Automatically generated by Pandoc 1.19.2.4
.\" .\"
.TH "mergerfs" "1" "2020\-08\-07" "mergerfs user manual" ""
.TH "mergerfs" "1" "2020\-08\-20" "mergerfs user manual" ""
.hy .hy
.SH NAME .SH NAME
.PP .PP
@ -778,6 +778,11 @@ Of all the branches on which the relative path exists choose the drive
with the most free space. with the most free space.
T} T}
T{ T{
eppfrd (existing path, percentage free random distribution)
T}@T{
Like \f[B]pfrd\f[] but limited to existing paths.
T}
T{
eprand (existing path, random) eprand (existing path, random)
T}@T{ T}@T{
Calls \f[B]epall\f[] and then randomizes. Calls \f[B]epall\f[] and then randomizes.
@ -843,11 +848,27 @@ Create: like \f[B]eplmfs\f[] but walk back the path if it fails to find
a branch at that level. a branch at that level.
T} T}
T{ T{
msppfrd (most shared path, percentage free random distribution)
T}@T{
Search: same as \f[B]eppfrd\f[].
Action: same as \f[B]eppfrd\f[].
Create: Like \f[B]eppfrd\f[] but will walk back the path if it fails to
find a branch at that level.
T}
T{
newest newest
T}@T{ T}@T{
Pick the file / directory with the largest mtime. Pick the file / directory with the largest mtime.
T} T}
T{ T{
pfrd (percentage free random distribution)
T}@T{
Search: same as \f[B]eppfrd\f[].
Action: same as \f[B]eppfrd\f[].
Create: Chooses a branch at random with the likelihood of selection
based on a branch\[aq]s available space relative to the total.
T}
T{
rand (random) rand (random)
T}@T{ T}@T{
Calls \f[B]all\f[] and then randomizes. Calls \f[B]all\f[] and then randomizes.

18
src/policy.cpp

@ -32,6 +32,7 @@ const std::vector<Policy> Policy::_policies_ =
(POLICY(eplfs,PRESERVES_PATH)) (POLICY(eplfs,PRESERVES_PATH))
(POLICY(eplus,PRESERVES_PATH)) (POLICY(eplus,PRESERVES_PATH))
(POLICY(epmfs,PRESERVES_PATH)) (POLICY(epmfs,PRESERVES_PATH))
(POLICY(eppfrd,PRESERVES_PATH))
(POLICY(eprand,PRESERVES_PATH)) (POLICY(eprand,PRESERVES_PATH))
(POLICY(erofs,DOESNT_PRESERVE_PATH)) (POLICY(erofs,DOESNT_PRESERVE_PATH))
(POLICY(ff,DOESNT_PRESERVE_PATH)) (POLICY(ff,DOESNT_PRESERVE_PATH))
@ -41,7 +42,9 @@ const std::vector<Policy> Policy::_policies_ =
(POLICY(msplfs,PRESERVES_PATH)) (POLICY(msplfs,PRESERVES_PATH))
(POLICY(msplus,PRESERVES_PATH)) (POLICY(msplus,PRESERVES_PATH))
(POLICY(mspmfs,PRESERVES_PATH)) (POLICY(mspmfs,PRESERVES_PATH))
(POLICY(msppfrd,PRESERVES_PATH))
(POLICY(newest,DOESNT_PRESERVE_PATH)) (POLICY(newest,DOESNT_PRESERVE_PATH))
(POLICY(pfrd,DOESNT_PRESERVE_PATH))
(POLICY(rand,DOESNT_PRESERVE_PATH)); (POLICY(rand,DOESNT_PRESERVE_PATH));
const Policy * const Policy::policies = &_policies_[1]; const Policy * const Policy::policies = &_policies_[1];
@ -55,6 +58,7 @@ CONST_POLICY(epff);
CONST_POLICY(eplfs); CONST_POLICY(eplfs);
CONST_POLICY(eplus); CONST_POLICY(eplus);
CONST_POLICY(epmfs); CONST_POLICY(epmfs);
CONST_POLICY(eppfrd);
CONST_POLICY(eprand); CONST_POLICY(eprand);
CONST_POLICY(erofs); CONST_POLICY(erofs);
CONST_POLICY(ff); CONST_POLICY(ff);
@ -64,19 +68,21 @@ CONST_POLICY(mfs);
CONST_POLICY(msplfs); CONST_POLICY(msplfs);
CONST_POLICY(msplus); CONST_POLICY(msplus);
CONST_POLICY(mspmfs); CONST_POLICY(mspmfs);
CONST_POLICY(msppfrd);
CONST_POLICY(newest); CONST_POLICY(newest);
CONST_POLICY(pfrd);
CONST_POLICY(rand); CONST_POLICY(rand);
const Policy& const Policy&
Policy::find(const std::string &str) Policy::find(const std::string &str)
{ {
for(int i = Enum::BEGIN; i != Enum::END; ++i)
{
if(policies[i] == str)
return policies[i];
}
for(int i = Enum::BEGIN; i != Enum::END; ++i)
{
if(policies[i] == str)
return policies[i];
}
return invalid;
return invalid;
} }
const Policy& const Policy&

9
src/policy.hpp

@ -37,6 +37,7 @@ public:
eplfs, eplfs,
eplus, eplus,
epmfs, epmfs,
eppfrd,
eprand, eprand,
erofs, erofs,
ff, ff,
@ -46,7 +47,9 @@ public:
msplfs, msplfs,
msplus, msplus,
mspmfs, mspmfs,
msppfrd,
newest, newest,
pfrd,
rand, rand,
END END
}; };
@ -117,6 +120,7 @@ public:
static int eplfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int eplfs(Category,const Branches&,const char *,cuint64_t,strvec*);
static int eplus(Category,const Branches&,const char *,cuint64_t,strvec*); static int eplus(Category,const Branches&,const char *,cuint64_t,strvec*);
static int epmfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int epmfs(Category,const Branches&,const char *,cuint64_t,strvec*);
static int eppfrd(Category,const Branches&,const char *,cuint64_t,strvec*);
static int eprand(Category,const Branches&,const char *,cuint64_t,strvec*); static int eprand(Category,const Branches&,const char *,cuint64_t,strvec*);
static int erofs(Category,const Branches&,const char *,cuint64_t,strvec*); static int erofs(Category,const Branches&,const char *,cuint64_t,strvec*);
static int ff(Category,const Branches&,const char *,cuint64_t,strvec*); static int ff(Category,const Branches&,const char *,cuint64_t,strvec*);
@ -126,7 +130,9 @@ public:
static int msplfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int msplfs(Category,const Branches&,const char *,cuint64_t,strvec*);
static int msplus(Category,const Branches&,const char *,cuint64_t,strvec*); static int msplus(Category,const Branches&,const char *,cuint64_t,strvec*);
static int mspmfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int mspmfs(Category,const Branches&,const char *,cuint64_t,strvec*);
static int msppfrd(Category,const Branches&,const char *,cuint64_t,strvec*);
static int newest(Category,const Branches&,const char *,cuint64_t,strvec*); static int newest(Category,const Branches&,const char *,cuint64_t,strvec*);
static int pfrd(Category,const Branches&,const char *,cuint64_t,strvec*);
static int rand(Category,const Branches&,const char *,cuint64_t,strvec*); static int rand(Category,const Branches&,const char *,cuint64_t,strvec*);
}; };
@ -198,6 +204,7 @@ public:
static const Policy &eplfs; static const Policy &eplfs;
static const Policy &eplus; static const Policy &eplus;
static const Policy &epmfs; static const Policy &epmfs;
static const Policy &eppfrd;
static const Policy &eprand; static const Policy &eprand;
static const Policy &erofs; static const Policy &erofs;
static const Policy &ff; static const Policy &ff;
@ -207,7 +214,9 @@ public:
static const Policy &msplfs; static const Policy &msplfs;
static const Policy &msplus; static const Policy &msplus;
static const Policy &mspmfs; static const Policy &mspmfs;
static const Policy &msppfrd;
static const Policy &newest; static const Policy &newest;
static const Policy &pfrd;
static const Policy &rand; static const Policy &rand;
}; };

273
src/policy_eppfrd.cpp

@ -0,0 +1,273 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "errno.hpp"
#include "fs_exists.hpp"
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rnd.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
using std::string;
using std::vector;
struct BranchInfo
{
uint64_t spaceavail;
const string *basepath;
};
typedef vector<BranchInfo> BranchInfoVec;
namespace eppfrd
{
static
int
get_branchinfo_create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int rv;
int error;
BranchInfo bi;
fs::info_t info;
const Branch *branch;
rwlock::ReadGuard guard(&branches_.lock);
*sum_ = 0;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nc())
error_and_continue(error,EROFS);
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
rv = fs::info(branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
*sum_ += info.spaceavail;
bi.spaceavail = info.spaceavail;
bi.basepath = &branch->path;
branchinfo_->push_back(bi);
}
return error;
}
static
int
get_branchinfo_action(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int rv;
int error;
BranchInfo bi;
fs::info_t info;
const Branch *branch;
rwlock::ReadGuard guard(&branches_.lock);
*sum_ = 0;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
branch = &branches_[i];
if(branch->ro())
error_and_continue(error,EROFS);
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
rv = fs::info(branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
*sum_ += info.spaceavail;
bi.spaceavail = info.spaceavail;
bi.basepath = &branch->path;
branchinfo_->push_back(bi);
}
return error;
}
static
int
get_branchinfo_search(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int rv;
BranchInfo bi;
uint64_t spaceavail;
const Branch *branch;
rwlock::ReadGuard guard(&branches_.lock);
*sum_ = 0;
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath_))
continue;
rv = fs::statvfs_cache_spaceavail(branch->path,&spaceavail);
if(rv == -1)
continue;
*sum_ += spaceavail;
bi.spaceavail = spaceavail;
bi.basepath = &branch->path;
branchinfo_->push_back(bi);
}
return ENOENT;
}
static
const
string*
get_branch(const BranchInfoVec &branchinfo_,
const uint64_t sum_)
{
uint64_t idx;
uint64_t threshold;
idx = 0;
threshold = RND::rand64(sum_);
for(size_t i = 0; i < branchinfo_.size(); i++)
{
idx += branchinfo_[i].spaceavail;
if(idx < threshold)
continue;
return branchinfo_[i].basepath;
}
return NULL;
}
static
int
create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
int error;
uint64_t sum;
const string *basepath;
BranchInfoVec branchinfo;
branchinfo.reserve(branches_.size());
error = eppfrd::get_branchinfo_create(branches_,fusepath_,minfreespace_,&branchinfo,&sum);
basepath = eppfrd::get_branch(branchinfo,sum);
if(basepath == NULL)
return (errno=error,-1);
paths_->push_back(*basepath);
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
int error;
uint64_t sum;
const string *basepath;
BranchInfoVec branchinfo;
branchinfo.reserve(branches_.size());
error = eppfrd::get_branchinfo_action(branches_,fusepath_,minfreespace_,&branchinfo,&sum);
basepath = eppfrd::get_branch(branchinfo,sum);
if(basepath == NULL)
return (errno=error,-1);
paths_->push_back(*basepath);
return 0;
}
static
int
search(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
int error;
uint64_t sum;
const string *basepath;
BranchInfoVec branchinfo;
branchinfo.reserve(branches_.size());
error = eppfrd::get_branchinfo_search(branches_,fusepath_,minfreespace_,&branchinfo,&sum);
basepath = eppfrd::get_branch(branchinfo,sum);
if(basepath == NULL)
return (errno=error,-1);
paths_->push_back(*basepath);
return 0;
}
}
int
Policy::Func::eppfrd(const Category type_,
const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
switch(type_)
{
case Category::CREATE:
return eppfrd::create(branches_,fusepath_,minfreespace_,paths_);
case Category::ACTION:
return eppfrd::action(branches_,fusepath_,minfreespace_,paths_);
default:
case Category::SEARCH:
return eppfrd::search(branches_,fusepath_,minfreespace_,paths_);
}
}

172
src/policy_msppfrd.cpp

@ -0,0 +1,172 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "errno.hpp"
#include "fs_exists.hpp"
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rnd.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
using std::string;
using std::vector;
struct BranchInfo
{
uint64_t spaceavail;
const string *basepath;
};
typedef vector<BranchInfo> BranchInfoVec;
namespace msppfrd
{
static
int
create_1(const Branches &branches_,
const string &fusepath_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int rv;
int error;
BranchInfo bi;
fs::info_t info;
const Branch *branch;
rwlock::ReadGuard guard(&branches_.lock);
*sum_ = 0;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nc())
error_and_continue(error,EROFS);
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
rv = fs::info(branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
*sum_ += info.spaceavail;
bi.spaceavail = info.spaceavail;
bi.basepath = &branch->path;
branchinfo_->push_back(bi);
}
return error;
}
static
int
get_branchinfo(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int error;
string fusepath;
fusepath = fusepath_;
do
{
error = msppfrd::create_1(branches_,fusepath,minfreespace_,branchinfo_,sum_);
if(branchinfo_->size())
return error;
fusepath = fs::path::dirname(fusepath);
}
while(!fusepath.empty());
return error;
}
static
const
string*
get_branch(const BranchInfoVec &branchinfo_,
const uint64_t sum_)
{
uint64_t idx;
uint64_t threshold;
idx = 0;
threshold = RND::rand64(sum_);
for(size_t i = 0; i < branchinfo_.size(); i++)
{
idx += branchinfo_[i].spaceavail;
if(idx < threshold)
continue;
return branchinfo_[i].basepath;
}
return NULL;
}
static
int
create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
int error;
uint64_t sum;
const string *basepath;
BranchInfoVec branchinfo;
branchinfo.reserve(branches_.size());
error = msppfrd::get_branchinfo(branches_,fusepath_,minfreespace_,&branchinfo,&sum);
basepath = msppfrd::get_branch(branchinfo,sum);
if(basepath == NULL)
return (errno=error,-1);
paths_->push_back(*basepath);
return 0;
}
}
int
Policy::Func::msppfrd(const Category type_,
const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
if(type_ == Category::CREATE)
return msppfrd::create(branches_,fusepath_,minfreespace_,paths_);
return Policy::Func::eppfrd(type_,branches_,fusepath_,minfreespace_,paths_);
}

140
src/policy_pfrd.cpp

@ -0,0 +1,140 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "errno.hpp"
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rnd.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
using std::string;
using std::vector;
struct BranchInfo
{
uint64_t spaceavail;
const string *basepath;
};
typedef vector<BranchInfo> BranchInfoVec;
namespace pfrd
{
static
int
get_branchinfo(const Branches &branches_,
const uint64_t minfreespace_,
BranchInfoVec *branchinfo_,
uint64_t *sum_)
{
int rv;
int error;
BranchInfo bi;
fs::info_t info;
const Branch *branch;
rwlock::ReadGuard guard(&branches_.lock);
*sum_ = 0;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nc())
error_and_continue(error,EROFS);
rv = fs::info(branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
*sum_ += info.spaceavail;
bi.spaceavail = info.spaceavail;
bi.basepath = &branch->path;
branchinfo_->push_back(bi);
}
return error;
}
static
const
string*
get_branch(const BranchInfoVec &branchinfo_,
const uint64_t sum_)
{
uint64_t idx;
uint64_t threshold;
idx = 0;
threshold = RND::rand64(sum_);
for(size_t i = 0; i < branchinfo_.size(); i++)
{
idx += branchinfo_[i].spaceavail;
if(idx < threshold)
continue;
return branchinfo_[i].basepath;
}
return NULL;
}
static
int
create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
int error;
uint64_t sum;
const string *basepath;
BranchInfoVec branchinfo;
branchinfo.reserve(branches_.size());
error = pfrd::get_branchinfo(branches_,minfreespace_,&branchinfo,&sum);
basepath = pfrd::get_branch(branchinfo,sum);
if(basepath == NULL)
return (errno=error,-1);
paths_->push_back(*basepath);
return 0;
}
}
int
Policy::Func::pfrd(const Category type_,
const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
if(type_ == Category::CREATE)
return pfrd::create(branches_,fusepath_,minfreespace_,paths_);
return Policy::Func::eppfrd(type_,branches_,fusepath_,minfreespace_,paths_);
}

55
src/rnd.cpp

@ -0,0 +1,55 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "rnd.hpp"
#include "wyhash.h"
#include <stdint.h>
#include <sys/time.h>
static uint64_t G_SEED;
static RND G_RND;
RND::RND()
{
struct timeval tv;
gettimeofday(&tv,NULL);
G_SEED = ((tv.tv_sec << 32) | (tv.tv_usec));
}
uint64_t
RND::rand64(void)
{
return wyrand(&G_SEED);
}
uint64_t
RND::rand64(const uint64_t max_)
{
return (wyrand(&G_SEED) % max_);
}
uint64_t
RND::rand64(const uint64_t min_,
const uint64_t max_)
{
return (min_ + (wyrand(&G_SEED) % (max_ - min_)));
}

33
src/rnd.hpp

@ -0,0 +1,33 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <stdint.h>
class RND
{
public:
RND();
public:
static uint64_t rand64(void);
static uint64_t rand64(const uint64_t max_);
static uint64_t rand64(const uint64_t min_,
const uint64_t max_);
};
Loading…
Cancel
Save