client: report root's quota in statfs

When user is mounted a quota-restricted inode
as the root, report that inode's quota status
as the filesystem statistics in statfs.

This allows us to have a fairly convincing illusion
that someone has a filesystem to themselves, when
they're really mounting a restricted part of
the larger global filesystem.

Fixes: http://tracker.ceph.com/issues/15599
Signed-off-by: John Spray <john.spray@redhat.com>
This commit is contained in:
John Spray 2016-05-11 13:18:23 +01:00
parent 30af2cd071
commit b6d2b6d1a5
3 changed files with 54 additions and 14 deletions

View File

@ -43,6 +43,25 @@ for example, to restrict client ``foo`` to ``mnt/bar`` directory, we will use. :
./ceph-fuse -n client.foo mnt -r /bar
Free space reporting
--------------------
By default, when a client is mounting a sub-directory, the used space (``df``)
will be calculated from the quota on that sub-directory, rather than reporting
the overall amount of space used on the cluster.
If you would like the client to report the overall usage of the filesystem,
and not just the quota usage on the sub-directory mounted, then set the
following config option on the client:
::
client quota df = false
If quotas are not enabled, or no quota is set on the sub-directory mounted,
then the overall usage of the filesystem will be reported irrespective of
the value of this setting.
OSD restriction
===============

View File

@ -8752,19 +8752,11 @@ int Client::statfs(const char *path, struct statvfs *stbuf)
tout(cct) << "statfs" << std::endl;
ceph_statfs stats;
Mutex lock("Client::statfs::lock");
Cond cond;
bool done;
int rval;
objecter->get_fs_stats(stats, new C_SafeCond(&lock, &cond, &done, &rval));
C_SaferCond cond;
objecter->get_fs_stats(stats, &cond);
client_lock.Unlock();
lock.Lock();
while (!done)
cond.Wait(lock);
lock.Unlock();
int rval = cond.wait();
client_lock.Lock();
memset(stbuf, 0, sizeof(*stbuf));
@ -8779,9 +8771,6 @@ int Client::statfs(const char *path, struct statvfs *stbuf)
const int CEPH_BLOCK_SHIFT = 22;
stbuf->f_frsize = 1 << CEPH_BLOCK_SHIFT;
stbuf->f_bsize = 1 << CEPH_BLOCK_SHIFT;
stbuf->f_blocks = stats.kb >> (CEPH_BLOCK_SHIFT - 10);
stbuf->f_bfree = stats.kb_avail >> (CEPH_BLOCK_SHIFT - 10);
stbuf->f_bavail = stats.kb_avail >> (CEPH_BLOCK_SHIFT - 10);
stbuf->f_files = stats.num_objects;
stbuf->f_ffree = -1;
stbuf->f_favail = -1;
@ -8789,6 +8778,37 @@ int Client::statfs(const char *path, struct statvfs *stbuf)
stbuf->f_flag = 0; // ??
stbuf->f_namemax = NAME_MAX;
// Usually quota_root will == root_ancestor, but if the mount root has no
// quota but we can see a parent of it that does have a quota, we'll
// respect that one instead.
assert(root != nullptr);
Inode *quota_root = get_quota_root(root);
// get_quota_root should always give us something if client quotas are
// enabled
assert(cct->_conf->client_quota == false || quota_root != nullptr);
if (quota_root && cct->_conf->client_quota_df && quota_root->quota.max_bytes) {
// Special case: if there is a size quota set on the Inode acting
// as the root for this client mount, then report the quota status
// as the filesystem statistics.
const fsblkcnt_t total = quota_root->quota.max_bytes >> CEPH_BLOCK_SHIFT;
const fsblkcnt_t used = quota_root->rstat.rbytes >> CEPH_BLOCK_SHIFT;
const fsblkcnt_t free = total - used;
stbuf->f_blocks = total;
stbuf->f_bfree = free;
stbuf->f_bavail = free;
} else {
// General case: report the overall RADOS cluster's statistics. Because
// multiple pools may be used without one filesystem namespace via
// layouts, this is the most correct thing we can do.
stbuf->f_blocks = stats.kb >> (CEPH_BLOCK_SHIFT - 10);
stbuf->f_bfree = stats.kb_avail >> (CEPH_BLOCK_SHIFT - 10);
stbuf->f_bavail = stats.kb_avail >> (CEPH_BLOCK_SHIFT - 10);
}
return rval;
}

View File

@ -382,6 +382,7 @@ OPTION(client_notify_timeout, OPT_INT, 10) // in seconds
OPTION(osd_client_watch_timeout, OPT_INT, 30) // in seconds
OPTION(client_caps_release_delay, OPT_INT, 5) // in seconds
OPTION(client_quota, OPT_BOOL, false)
OPTION(client_quota_df, OPT_BOOL, true) // use quota for df on subdir mounts
OPTION(client_oc, OPT_BOOL, true)
OPTION(client_oc_size, OPT_INT, 1024*1024* 200) // MB * n
OPTION(client_oc_max_dirty, OPT_INT, 1024*1024* 100) // MB * n (dirty OR tx.. bigish)