[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