Browse Source

Add LUP/EPLUP policies

pull/1601/head
François-Xavier Payet 5 days ago
parent
commit
fc26b35e46
  1. 6
      mkdocs/docs/config/functions_categories_policies.md
  2. 8
      src/policies.cpp
  3. 8
      src/policies.hpp
  4. 236
      src/policy_eplup.cpp
  5. 70
      src/policy_eplup.hpp
  6. 234
      src/policy_lup.cpp
  7. 70
      src/policy_lup.hpp

6
mkdocs/docs/config/functions_categories_policies.md

@ -90,7 +90,7 @@ suddenly become read-only when it encounters an error.
Policies, as described below, are of two basic classifications. `path
preserving` and `non-path preserving`.
All policies which start with `ep` (`epff`, `eplfs`, `eplus`, `epmfs`,
All policies which start with `ep` (`epff`, `eplfs`, `eplus`, `eplup`, `epmfs`,
`eprand`) are `path preserving`. `ep` stands for `existing path`.
A path preserving policy will only consider branches where the relative
@ -116,6 +116,8 @@ but it makes things a bit more uniform.
| mfs (most free space) | Pick the branch with the most available free space. |
| ff (first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Pick the branch with the least available free space. |
| lup (least used percent) | Pick the branch with the least used
percentage of space |
| lus (least used space) | Pick the branch with the least used space. |
| all | Search: For **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| msppfrd (most shared path, percentage free random distribution) | Like **eppfrd** but if it fails to find a branch it will try again with the parent directory. Continues this pattern till finding one. |
@ -127,6 +129,8 @@ but it makes things a bit more uniform.
| eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. |
| 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 branch with the least free space. |
| eplup (existing path, least used percent) | Of all the branches on which the relative path exists choose the branch with the least used
percentage of space |
| eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the branch with the least used space. |
| epall (existing path, all) | 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). |
| newest | Pick the file / directory with the largest mtime. |

8
src/policies.cpp

@ -28,6 +28,7 @@
FUNC(epff) \
FUNC(eplfs) \
FUNC(eplus) \
FUNC(eplup) \
FUNC(epmfs) \
FUNC(eppfrd) \
FUNC(eprand) \
@ -35,6 +36,7 @@
FUNC(ff) \
FUNC(lfs) \
FUNC(lus) \
FUNC(lup) \
FUNC(mfs) \
FUNC(msplfs) \
FUNC(msplus) \
@ -73,6 +75,7 @@ Policy::EPAll::Action Policies::Action::epall;
Policy::EPFF::Action Policies::Action::epff;
Policy::EPLFS::Action Policies::Action::eplfs;
Policy::EPLUS::Action Policies::Action::eplus;
Policy::EPLUP::Action Policies::Action::eplup;
Policy::EPMFS::Action Policies::Action::epmfs;
Policy::EPPFRD::Action Policies::Action::eppfrd;
Policy::EPRand::Action Policies::Action::eprand;
@ -80,6 +83,7 @@ Policy::ERoFS::Action Policies::Action::erofs;
Policy::FF::Action Policies::Action::ff;
Policy::LFS::Action Policies::Action::lfs;
Policy::LUS::Action Policies::Action::lus;
Policy::LUP::Action Policies::Action::lup;
Policy::MFS::Action Policies::Action::mfs;
Policy::MSPLFS::Action Policies::Action::msplfs;
Policy::MSPLUS::Action Policies::Action::msplus;
@ -94,6 +98,7 @@ Policy::EPAll::Create Policies::Create::epall;
Policy::EPFF::Create Policies::Create::epff;
Policy::EPLFS::Create Policies::Create::eplfs;
Policy::EPLUS::Create Policies::Create::eplus;
Policy::EPLUP::Create Policies::Create::eplup;
Policy::EPMFS::Create Policies::Create::epmfs;
Policy::EPPFRD::Create Policies::Create::eppfrd;
Policy::EPRand::Create Policies::Create::eprand;
@ -101,6 +106,7 @@ Policy::ERoFS::Create Policies::Create::erofs;
Policy::FF::Create Policies::Create::ff;
Policy::LFS::Create Policies::Create::lfs;
Policy::LUS::Create Policies::Create::lus;
Policy::LUP::Create Policies::Create::lup;
Policy::MFS::Create Policies::Create::mfs;
Policy::MSPLFS::Create Policies::Create::msplfs;
Policy::MSPLUS::Create Policies::Create::msplus;
@ -115,6 +121,7 @@ Policy::EPAll::Search Policies::Search::epall;
Policy::EPFF::Search Policies::Search::epff;
Policy::EPLFS::Search Policies::Search::eplfs;
Policy::EPLUS::Search Policies::Search::eplus;
Policy::EPLUP::Search Policies::Search::eplup;
Policy::EPMFS::Search Policies::Search::epmfs;
Policy::EPPFRD::Search Policies::Search::eppfrd;
Policy::EPRand::Search Policies::Search::eprand;
@ -122,6 +129,7 @@ Policy::ERoFS::Search Policies::Search::erofs;
Policy::FF::Search Policies::Search::ff;
Policy::LFS::Search Policies::Search::lfs;
Policy::LUS::Search Policies::Search::lus;
Policy::LUP::Search Policies::Search::lup;
Policy::MFS::Search Policies::Search::mfs;
Policy::MSPLFS::Search Policies::Search::msplfs;
Policy::MSPLUS::Search Policies::Search::msplus;

8
src/policies.hpp

@ -23,6 +23,7 @@
#include "policy_epff.hpp"
#include "policy_eplfs.hpp"
#include "policy_eplus.hpp"
#include "policy_eplup.hpp"
#include "policy_epmfs.hpp"
#include "policy_eppfrd.hpp"
#include "policy_eprand.hpp"
@ -30,6 +31,7 @@
#include "policy_ff.hpp"
#include "policy_lfs.hpp"
#include "policy_lus.hpp"
#include "policy_lup.hpp"
#include "policy_mfs.hpp"
#include "policy_msplfs.hpp"
#include "policy_msplus.hpp"
@ -49,6 +51,7 @@ struct Policies
static Policy::EPAll::Action epall;
static Policy::EPFF::Action epff;
static Policy::EPLFS::Action eplfs;
static Policy::EPLUP::Action eplup;
static Policy::EPLUS::Action eplus;
static Policy::EPMFS::Action epmfs;
static Policy::EPPFRD::Action eppfrd;
@ -56,6 +59,7 @@ struct Policies
static Policy::ERoFS::Action erofs;
static Policy::FF::Action ff;
static Policy::LFS::Action lfs;
static Policy::LUP::Action lup;
static Policy::LUS::Action lus;
static Policy::MFS::Action mfs;
static Policy::MSPLFS::Action msplfs;
@ -75,6 +79,7 @@ struct Policies
static Policy::EPAll::Create epall;
static Policy::EPFF::Create epff;
static Policy::EPLFS::Create eplfs;
static Policy::EPLUP::Create eplup;
static Policy::EPLUS::Create eplus;
static Policy::EPMFS::Create epmfs;
static Policy::EPPFRD::Create eppfrd;
@ -82,6 +87,7 @@ struct Policies
static Policy::ERoFS::Create erofs;
static Policy::FF::Create ff;
static Policy::LFS::Create lfs;
static Policy::LUP::Create lup;
static Policy::LUS::Create lus;
static Policy::MFS::Create mfs;
static Policy::MSPLFS::Create msplfs;
@ -101,6 +107,7 @@ struct Policies
static Policy::EPAll::Search epall;
static Policy::EPFF::Search epff;
static Policy::EPLFS::Search eplfs;
static Policy::EPLUP::Search eplup;
static Policy::EPLUS::Search eplus;
static Policy::EPMFS::Search epmfs;
static Policy::EPPFRD::Search eppfrd;
@ -108,6 +115,7 @@ struct Policies
static Policy::ERoFS::Search erofs;
static Policy::FF::Search ff;
static Policy::LFS::Search lfs;
static Policy::LUP::Search lup;
static Policy::LUS::Search lus;
static Policy::MFS::Search mfs;
static Policy::MSPLFS::Search msplfs;

236
src/policy_eplup.cpp

@ -0,0 +1,236 @@
/*
Copyright (c) 2025, François-Xavier Payet <fx@payet.io>
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 "policy_eplup.hpp"
#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 "rwlock.hpp"
#include <limits>
#include <string>
using std::string;
static int
_create(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_)
{
int rv;
int error;
fs::info_t info;
Branch *obranch;
obranch = nullptr;
error = ENOENT;
uint64_t best_used = 0;
uint64_t best_total = 1; /* avoid div-by-zero */
for (auto &branch : *branches_)
{
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 < 0)
error_and_continue(error, ENOENT);
if (info.readonly)
error_and_continue(error, EROFS);
if (info.spaceavail < branch.minfreespace())
error_and_continue(error, ENOSPC);
uint64_t used = info.spaceused;
uint64_t total = info.spaceused + info.spaceavail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -error;
paths_.emplace_back(obranch);
return 0;
}
static int
_action(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_)
{
int rv;
int error;
fs::info_t info;
Branch *obranch;
obranch = nullptr;
error = ENOENT;
uint64_t best_used = 0;
uint64_t best_total = 1;
for (auto &branch : *branches_)
{
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 < 0)
error_and_continue(error, ENOENT);
if (info.readonly)
error_and_continue(error, EROFS);
uint64_t used = info.spaceused;
uint64_t total = info.spaceused + info.spaceavail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -error;
paths_.emplace_back(obranch);
return 0;
}
static int
_search(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_)
{
int rv;
uint64_t used;
uint64_t avail;
uint64_t best_used = 0;
uint64_t best_total = 1;
Branch *obranch;
obranch = nullptr;
for (auto &branch : *branches_)
{
if (!fs::exists(branch.path, fusepath_))
continue;
rv = fs::statvfs_cache_spaceused(branch.path, &used);
if (rv < 0)
continue;
rv = fs::statvfs_cache_spaceavail(branch.path, &avail);
if (rv < 0)
continue;
uint64_t total = used + avail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -ENOENT;
paths_.emplace_back(obranch);
return 0;
}
int Policy::EPLUP::Action::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_action(branches_, fusepath_, paths_);
}
int Policy::EPLUP::Create::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_create(branches_, fusepath_, paths_);
}
int Policy::EPLUP::Search::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_search(branches_, fusepath_, paths_);
}

70
src/policy_eplup.hpp

@ -0,0 +1,70 @@
/*
ISC License
Copyright (c) 2025, François-Xavier Payet <fx@payet.io>
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 "policy.hpp"
namespace Policy
{
namespace EPLUP
{
class Action final : public Policy::ActionImpl
{
public:
Action()
: Policy::ActionImpl("eplup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
};
class Create final : public Policy::CreateImpl
{
public:
Create()
: Policy::CreateImpl("eplup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
bool path_preserving(void) const final { return true; }
};
class Search final : public Policy::SearchImpl
{
public:
Search()
: Policy::SearchImpl("eplup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
};
}
}

234
src/policy_lup.cpp

@ -0,0 +1,234 @@
/*
Copyright (c) 2025, François-Xavier Payet <fx@payet.io>
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 "policy_lup.hpp"
#include "errno.hpp"
#include "fs_exists.hpp"
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policies.hpp"
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
using std::string;
static int
_action(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_)
{
int rv;
int error;
fs::info_t info;
Branch *obranch;
obranch = nullptr;
error = ENOENT;
uint64_t best_used = 0;
uint64_t best_total = 1;
for (auto &branch : *branches_)
{
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 < 0)
error_and_continue(error, ENOENT);
if (info.readonly)
error_and_continue(error, EROFS);
uint64_t used = info.spaceused;
uint64_t total = info.spaceused + info.spaceavail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -error;
paths_.push_back(obranch);
return 0;
}
static int
_search(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_)
{
int rv;
uint64_t used;
uint64_t avail;
uint64_t best_used = 0;
uint64_t best_total = 1;
Branch *obranch;
obranch = nullptr;
for (auto &branch : *branches_)
{
if (!fs::exists(branch.path, fusepath_))
continue;
rv = fs::statvfs_cache_spaceused(branch.path, &used);
if (rv < 0)
continue;
rv = fs::statvfs_cache_spaceavail(branch.path, &avail);
if (rv < 0)
continue;
uint64_t total = used + avail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -ENOENT;
paths_.push_back(obranch);
return 0;
}
static int
_create(const Branches::Ptr &branches_,
std::vector<Branch *> &paths_)
{
int rv;
int error;
fs::info_t info;
Branch *obranch;
obranch = nullptr;
error = ENOENT;
uint64_t best_used = 0;
uint64_t best_total = 1;
for (auto &branch : *branches_)
{
if (branch.ro_or_nc())
error_and_continue(error, EROFS);
rv = fs::info(branch.path, &info);
if (rv < 0)
error_and_continue(error, ENOENT);
if (info.readonly)
error_and_continue(error, EROFS);
if (info.spaceavail < branch.minfreespace())
error_and_continue(error, ENOSPC);
uint64_t used = info.spaceused;
uint64_t total = info.spaceused + info.spaceavail;
if (total == 0)
{
used = 0;
total = 1;
}
if (obranch == nullptr)
{
best_used = used;
best_total = total;
obranch = &branch;
continue;
}
unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total;
unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total;
if (lhs >= rhs)
continue;
best_used = used;
best_total = total;
obranch = &branch;
}
if (obranch == nullptr)
return -error;
paths_.push_back(obranch);
return 0;
}
int Policy::LUP::Action::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_action(branches_, fusepath_, paths_);
}
int Policy::LUP::Create::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_create(branches_, paths_);
}
int Policy::LUP::Search::operator()(const Branches::Ptr &branches_,
const fs::path &fusepath_,
std::vector<Branch *> &paths_) const
{
return ::_search(branches_, fusepath_, paths_);
}

70
src/policy_lup.hpp

@ -0,0 +1,70 @@
/*
ISC License
Copyright (c) 2025, François-Xavier Payet <fx@payet.io>
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 "policy.hpp"
namespace Policy
{
namespace LUP
{
class Action final : public Policy::ActionImpl
{
public:
Action()
: Policy::ActionImpl("lup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
};
class Create final : public Policy::CreateImpl
{
public:
Create()
: Policy::CreateImpl("lup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
bool path_preserving() const final { return false; }
};
class Search final : public Policy::SearchImpl
{
public:
Search()
: Policy::SearchImpl("lup")
{
}
public:
int operator()(const Branches::Ptr &,
const fs::path &,
std::vector<Branch *> &) const final;
};
}
}
Loading…
Cancel
Save