[Gluster-devel] The QUOTA xlator V0.2 codename "dual fire" (inlined patch included)
Angel
clist at uah.es
Tue Jan 22 12:03:08 UTC 2008
Hi all
- Ive just discovered that devel-list strips attachments from messages...
Im proudly present my QUOTA xlator v0.2 'dual fire'. (patch against mainline-2.5 tla636)
"The Quota translator imposes a limit on the underlaying storage allowing to limit disk usage
of subvolumes directly on filesystem calls or by means of informing schedulers on stats mops."
I like to thank avati for his great help on getting me get the inners of glusterFS.
This is the present features (V0.2):
- Check limits and reports limited values from stats mops to upper schedulers (unify et al).
- Cheks limits and reports limited values on statsvfs fops (for "df " command)
- Check limits on writes, reporting ENOSPC on hitting max-size writes
- Checks on unliks computing recovered space.
- Also checks for multiple hardlinks and account on last hardlink unlinked.
Changelog:
Changed unlink tracking scheme "stat before and unlink after" that fired unlink on stat_cbk
on pipeline firing both calls for performance gains.
Changed passing struct stat *buf from posix-storage across call to a private copy on quota xlator
Include docs at /doc/examples/quota.vol
Comments are welcome.
Keep up the Good Work(tm) GlusterFS guys!!!
Regards, Angel
--
------------------------------------------------
Clist UAH (Angel Alvarez)
------------------------------------------------
diff -pruN glusterfs-tla636/configure.ac glusterfs-tla636-quota-xlator-0.1/configure.ac
--- glusterfs-tla636/configure.ac 2008-01-16 17:19:48.290785081 +0100
+++ glusterfs-tla636-quota-xlator-0.1/configure.ac 2008-01-16 22:43:06.896248606 +0100
@@ -64,6 +64,8 @@ AC_CONFIG_FILES([Makefile
xlators/features/fixed-id/src/Makefile
xlators/features/trash/Makefile
xlators/features/trash/src/Makefile
+ xlators/features/quota/Makefile
+ xlators/features/quota/src/Makefile
xlators/encryption/Makefile
xlators/encryption/rot-13/Makefile
xlators/encryption/rot-13/src/Makefile
diff -pruN glusterfs-tla636/doc/examples/quota.vol glusterfs-tla636-quota-xlator-0.1/doc/examples/quota.vol
--- glusterfs-tla636/doc/examples/quota.vol 1970-01-01 01:00:00.000000000 +0100
+++ glusterfs-tla636-quota-xlator-0.1/doc/examples/quota.vol 2008-01-19 22:27:50.234951911 +0100
@@ -0,0 +1,21 @@
+volume brick
+ type storage/posix # POSIX FS translator
+ option directory /home/export # Export this directory
+end-volume
+
+### 'Quota' feature should be added on the server side (as posix volume as subvolume) where it can be used to limit max usage,
+on the underlaying block device.
+volume quota
+ type features/quota
+ subvolumes brick
+ option max-size 50M # Limit underlaying volume to 50MB of space
+ option enforce-level # Limit upper schedulers or limit all oprations (CURRENTLY NOT IMPLEMENTED)
+end-volume
+
+volume server
+ type protocol/server
+ subvolumes quota brick
+ option transport-type tcp/server
+ option auth.ip.brick.allow 192.168.* # Allow access to "brick" volume
+ option auth.ip.quota.allow 192.168.* # Allow access to "quota" volume (exporting 50MB disk space)
+end-volume
diff -pruN glusterfs-tla636/doc/translator-option.txt glusterfs-tla636-quota-xlator-0.1/doc/translator-option.txt
--- glusterfs-tla636/doc/translator-option.txt 2008-01-16 17:19:48.790813576 +0100
+++ glusterfs-tla636-quota-xlator-0.1/doc/translator-option.txt 2008-01-19 22:22:12.215689291 +0100
@@ -151,6 +151,10 @@
option fixed-uid <n> [if not set, not used]
option fixed-gid <n> [if not set, not used]
+# features/quota:
+ option max-size <n> [if not set, not used ]
+ option enforce-level [0|1] (1)
+
# features/filter:
- NO OPTIONS
diff -pruN glusterfs-tla636/xlators/features/Makefile.am glusterfs-tla636-quota-xlator-0.1/xlators/features/Makefile.am
--- glusterfs-tla636/xlators/features/Makefile.am 2008-01-16 17:19:52.291013041 +0100
+++ glusterfs-tla636-quota-xlator-0.1/xlators/features/Makefile.am 2008-01-16 22:24:35.832932716 +0100
@@ -1 +1 @@
-SUBDIRS = filter fixed-id posix-locks trash
+SUBDIRS = filter fixed-id posix-locks trash quota
diff -pruN glusterfs-tla636/xlators/features/quota/Makefile.am glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/Makefile.am
--- glusterfs-tla636/xlators/features/quota/Makefile.am 1970-01-01 01:00:00.000000000 +0100
+++ glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/Makefile.am 2008-01-16 17:19:51.000000000 +0100
@@ -0,0 +1 @@
+SUBDIRS = src
diff -pruN glusterfs-tla636/xlators/features/quota/src/Makefile.am glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/src/Makefile.am
--- glusterfs-tla636/xlators/features/quota/src/Makefile.am 1970-01-01 01:00:00.000000000 +0100
+++ glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/src/Makefile.am 2008-01-16 22:59:55.453723021 +0100
@@ -0,0 +1,11 @@
+
+xlator_PROGRAMS = quota.so
+xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/quota
+
+quota_so_SOURCES = quota.c
+
+AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\
+ -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles
+
+CLEANFILES = *~
+
diff -pruN glusterfs-tla636/xlators/features/quota/src/quota.c glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/src/quota.c
--- glusterfs-tla636/xlators/features/quota/src/quota.c 1970-01-01 01:00:00.000000000 +0100
+++ glusterfs-tla636-quota-xlator-0.1/xlators/features/quota/src/quota.c 2008-01-21 23:32:37.194968044 +0100
@@ -0,0 +1,482 @@
+/*
+ Copyright (c) 2006, 2007 Z RESEARCH, Inc. <http://www.zresearch.com>
+ This file is part of GlusterFS.
+
+ GlusterFS 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; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * xlators/features/quota :
+ * This translator imposes quota over file operations
+ * mostly based on trace xlator :-).
+ * Derive initially by Angel Alvarez clist at uah.es
+ */
+
+#include <time.h>
+#include <errno.h>
+#include "glusterfs.h"
+#include "xlator.h"
+#include "defaults.h"
+
+
+#define ERR_EINVAL_NORETURN(cond) \
+do \
+ { \
+ if ((cond)) \
+ { \
+ gf_log ("ERROR", \
+ GF_LOG_ERROR, \
+ "%s: %s: (%s) is true", \
+ __FILE__, __FUNCTION__, #cond); \
+ } \
+ } while (0)
+
+extern int32_t errno;
+
+#define _FORMAT_WARN(domain, log_level, format, args...) printf ("__DEBUG__" format, ##args);
+
+struct quota_private {
+ int32_t debug_flag;
+ int64_t storage_size;
+ int64_t disk_usage;
+ int64_t free_disk;
+};
+
+void
+quota_add (xlator_t *this,
+ int64_t quantity)
+{
+ struct quota_private *private = NULL;
+
+ if (this) {
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "QUOTA add (%d)",
+ quantity);
+ private = this->private;
+ private->disk_usage += quantity;
+ if ( private->disk_usage > private->storage_size ) {
+ private->free_disk = 0;
+ } else {
+ private->free_disk = private->storage_size - private->disk_usage;
+ }
+ }
+}
+
+void
+quota_sub (xlator_t *this,
+ int64_t quantity)
+{
+ struct quota_private *private = NULL;
+
+ if (this) {
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "QUOTA sub (%d)",
+ quantity);
+ private = this->private;
+ private->disk_usage -= quantity;
+ if ( private->disk_usage < 0 ) {
+ private->free_disk = private->storage_size;
+ } else {
+ private->free_disk = private->storage_size - private->disk_usage;
+ }
+ }
+}
+
+int64_t
+quota_check (xlator_t *this)
+{
+ return ((struct quota_private *)this->private)->free_disk;
+}
+
+void
+quota_alter_statvfs (xlator_t *this,
+ struct statvfs *buf)
+{
+ struct quota_private *private = NULL;
+
+ private = this->private;
+
+ // set filesystem max blocks
+ if ( buf->f_blocks > (private->storage_size / buf->f_frsize) ) buf->f_blocks = (private->storage_size / buf->f_frsize);
+ // set filesystem free blocks for users
+ buf->f_bfree = private->free_disk / buf->f_frsize;
+ // FIXME: set filesystem free blocks for root
+ buf->f_bavail = private->free_disk / buf->f_frsize;
+
+}
+
+static int32_t
+quota_writev_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct stat *buf)
+{
+ int64_t disk_size = quota_check(this);
+
+ ERR_EINVAL_NORETURN (!this);
+
+ // Record bytes written on success
+ if ( op_ret > 0 ) {
+ quota_add( this, op_ret );
+ }
+ // If quota is over return error
+ if ( disk_size == 0 ) {
+ op_ret = -1;
+ op_errno = ENOSPC;
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "No space available! (*this=%p, op_ret=%d, op_errno=%d)",
+ this, op_ret, op_errno);
+ }
+ STACK_UNWIND (frame, op_ret, op_errno, buf);
+ return 0;
+}
+
+static int32_t
+quota_writev (call_frame_t *frame,
+ xlator_t *this,
+ fd_t *fd,
+ struct iovec *vector,
+ int32_t count,
+ off_t offset)
+{
+ ERR_EINVAL_NORETURN (!this || !fd || !vector || (count < 1));
+
+ STACK_WIND (frame,
+ quota_writev_cbk,
+ FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->writev,
+ fd, vector, count,offset);
+ return 0;
+}
+
+
+static int32_t
+quota_unlink_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno)
+{
+ struct stat *mybuf = NULL;
+
+ ERR_EINVAL_NORETURN (!this );
+
+ // get mybuf from frame->local and sanitize it
+ mybuf = frame->local;
+ frame->local = NULL;
+
+ if (op_ret >= 0) {
+ // Unlink Ok, well lets see if there were spare links for the data
+ if ( mybuf->st_nlink == 1 ) quota_sub(this,mybuf->st_size); // no hardlinks left account bytes freed
+ } else {
+ // unlik failed? can not account bytes freed
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "unlink failed! (*this=%p, op_ret=%d, op_errno=%d)",
+ this, op_ret, op_errno);
+ }
+ // free frame->local, so glusterfs hackers dont blame on me!!
+ free(mybuf);
+ // return to caller
+ STACK_UNWIND (frame, op_ret, op_errno);
+ return 0;
+}
+
+static int32_t
+quota_stat_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct stat *buf)
+{
+ ERR_EINVAL_NORETURN (!this);
+
+ if (frame->local == NULL) {
+
+ // normal unwind
+ STACK_UNWIND (frame, op_ret, op_errno, buf);
+
+ } else {
+
+ // private affairs... i was fired by unlink
+ // copy stat buf onto frame->local (mybuf)
+
+ memcpy(frame->local,buf, sizeof(struct stat));
+
+ // no stack unwinding here
+ }
+ return 0;
+}
+
+static int32_t
+quota_unlink (call_frame_t *frame,
+ xlator_t *this,
+ loc_t *loc)
+{
+ struct stat *mybuf = calloc (1, sizeof (*mybuf));
+
+ ERR_EINVAL_NORETURN (!this || !loc);
+
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ " Unlink request path=%s, inode=%p ",
+ loc->path, loc->inode);
+
+ // store mybuf on frame->local to preserve it
+ frame->local=mybuf;
+ // Fire and forget,stat and unlink
+ STACK_WIND (frame,
+ quota_stat_cbk,
+ FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->stat,
+ loc);
+ STACK_WIND (frame,
+ quota_unlink_cbk,
+ FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->unlink,
+ loc);
+ return 0;
+}
+
+static int32_t
+quota_statfs_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct statvfs *buf)
+{
+
+ ERR_EINVAL_NORETURN (!this);
+
+ if ( op_ret >= 0 ) {
+ quota_alter_statvfs (this, buf);
+ }
+ STACK_UNWIND (frame, op_ret, op_errno, buf);
+ return 0;
+}
+
+static int32_t
+quota_statfs (call_frame_t *frame,
+ xlator_t *this,
+ loc_t *loc)
+{
+ ERR_EINVAL_NORETURN (!this || !loc);
+ STACK_WIND (frame,
+ quota_statfs_cbk,
+ FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->statfs,
+ loc);
+ return 0;
+}
+
+int32_t
+notify (xlator_t *this,
+ int32_t event,
+ void *data,
+ ...)
+{
+ struct quota_private *private = NULL;
+
+ private = this->private;
+
+ if (!private)
+ return 0;
+
+ switch (event) {
+ case GF_EVENT_CHILD_UP:
+ {
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "Notify Event CHILD_UP (%d)\n", event);
+ default_notify (this, event, data);
+ }
+ break;
+ case GF_EVENT_CHILD_DOWN:
+ {
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "Notify Event CHILD_DOWN (%d)\n", event);
+ default_notify (this, event, data);
+ }
+ break;
+ default:
+ {
+ default_notify (this, event, data);
+ }
+ }
+
+ return 0;
+}
+
+int32_t
+init (xlator_t *this)
+{
+
+void
+gf_log_xlator (xlator_t *this) {
+ int32_t len;
+ char *buf;
+
+ if (!this)
+ return;
+
+ len = dict_serialized_length (this->options);
+ buf = alloca (len);
+ dict_serialize (this->options, buf);
+
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "init name=%s, next=%s, parent=%s ",
+ this->name, this->next->name, this->parent->name);
+}
+
+ dict_t *options = this->options;
+
+ struct quota_private *private = calloc (1, sizeof (*private));
+
+ if (!this)
+ return -1;
+ if (!this->children) {
+ gf_log (this->name,
+ GF_LOG_ERROR,
+ "quota translator requires one subvolume");
+ return -1;
+ }
+
+ if (this->children->next) {
+ gf_log (this->name,
+ GF_LOG_ERROR,
+ "quota translator does not support more than one sub-volume");
+ return -1;
+ }
+ // If parameter "max-size" is set init vars
+ if (dict_get (this->options, "max-size")) {
+ private->storage_size = gf_str_to_long_long (data_to_str (dict_get (this->options,"max-size")));
+ private->disk_usage = 0;
+ private->free_disk = private->storage_size;
+
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "Max filesystem size reported = %s",
+ dict_get (this->options,"max-size"));
+ } else {
+ private->storage_size = 0;
+ }
+
+ //gf_log_set_loglevel (GF_LOG_DEBUG);
+
+
+ gf_log_xlator(this);
+
+ /* Set this translator's inode table pointer to child node's pointer. */
+ this->itable = FIRST_CHILD (this)->itable;
+
+ this->private = (void *)private;
+
+
+ return 0;
+}
+
+
+void
+fini (xlator_t *this)
+{
+ if (!this)
+ return;
+
+
+ /* Free up the dictionary options */
+ dict_destroy (FIRST_CHILD(this)->options);
+
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "quota translator unloaded");
+ return;
+}
+
+struct xlator_fops fops = {
+ .unlink = quota_unlink,
+ .writev = quota_writev,
+ .statfs = quota_statfs,
+};
+
+static int32_t
+quota_stats_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct xlator_stats *stats)
+{
+ struct quota_private *private = NULL;
+
+
+ private = this->private;
+
+ // Here we control space available to upper modules
+ if (private->storage_size > 0) {
+ // Show original values
+ gf_log (this->name,
+ GF_LOG_DEBUG,
+ "quota_stats_cbk (Limiting storage in stats_cbk)\n");
+ printf ("Pre Limit: %lld Total: %lld Free: %lld Used: %lld \n",
+ private->storage_size,
+ stats->total_disk_size,
+ stats->free_disk,
+ stats->disk_usage);
+ // Alter values for enforce limitation
+ stats->total_disk_size = private->storage_size;
+ stats->free_disk = private->free_disk;
+ stats->disk_usage = private->disk_usage;
+
+ printf ("Post Limit: %lld Total: %lld Free: %lld Used: %lld \n",
+ private->storage_size,
+ stats->total_disk_size,
+ stats->free_disk,
+ stats->disk_usage);
+ }
+ STACK_UNWIND (frame, op_ret, op_errno, stats);
+ return 0;
+}
+
+static int32_t
+quota_stats (call_frame_t *frame,
+ xlator_t *this,
+ int32_t flags)
+{
+ ERR_EINVAL_NORETURN (!this);
+
+ {
+ gf_log (this->name, GF_LOG_DEBUG, "quota_stats (*this=%p, flags=%d\n", this, flags);
+
+ STACK_WIND (frame,
+ quota_stats_cbk,
+ FIRST_CHILD(this),
+ FIRST_CHILD(this)->mops->stats,
+ flags);
+ }
+ return 0;
+}
+
+struct xlator_mops mops = {
+ .stats = quota_stats
+};
More information about the Gluster-devel
mailing list