diff options
Diffstat (limited to 'xlators/features/quota/src/quota.c')
| -rw-r--r-- | xlators/features/quota/src/quota.c | 435 |
1 files changed, 376 insertions, 59 deletions
diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c index 77218bc4e..c527e7ca7 100644 --- a/xlators/features/quota/src/quota.c +++ b/xlators/features/quota/src/quota.c @@ -1,22 +1,12 @@ /* - Copyright (c) 2008-2011 Gluster, Inc. <http://www.gluster.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/>. -*/ + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ #include <fnmatch.h> #include "quota.h" @@ -136,6 +126,7 @@ quota_local_cleanup (xlator_t *this, quota_local_t *local) inode_unref (local->inode); LOCK_DESTROY (&local->lock); + mem_put (local); out: return 0; } @@ -497,6 +488,8 @@ quota_get_limit_value (inode_t *inode, xlator_t *this, int64_t *n) } out: + GF_FREE (path); + return ret; } @@ -577,16 +570,20 @@ quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *dict, struct iatt *postparent) { - int32_t ret = -1; - char found = 0; - quota_local_t *local = NULL; - quota_inode_ctx_t *ctx = NULL; - quota_dentry_t *dentry = NULL; - int64_t *size = 0; - uint64_t value = 0; + int32_t ret = -1; + char found = 0; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + quota_dentry_t *dentry = NULL; + int64_t *size = 0; + uint64_t value = 0; + limits_t *limit_node = NULL; + quota_priv_t *priv = NULL; local = frame->local; + priv = this->private; + inode_ctx_get (inode, this, &value); ctx = (quota_inode_ctx_t *)(unsigned long)value; @@ -597,6 +594,18 @@ quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto unwind; } + LOCK (&priv->lock); + { + list_for_each_entry (limit_node, &priv->limit_head, + limit_list) { + if (strcmp (local->loc.path, limit_node->path) == 0) { + uuid_copy (limit_node->gfid, buf->ia_gfid); + break; + } + } + } + UNLOCK (&priv->lock); + ret = quota_inode_ctx_get (local->loc.inode, local->limit, this, dict, buf, &ctx, 1); if ((ret == -1) || (ctx == NULL)) { @@ -2685,7 +2694,6 @@ int quota_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict, int flags, dict_t *xdata) { - data_pair_t *trav = NULL; int op_errno = EINVAL; int op_ret = -1; @@ -2694,7 +2702,7 @@ quota_setxattr (call_frame_t *frame, xlator_t *this, VALIDATE_OR_GOTO (loc, err); GF_IF_INTERNAL_XATTR_GOTO ("trusted.glusterfs.quota*", dict, - trav, op_errno, err); + op_errno, err); STACK_WIND (frame, quota_setxattr_cbk, FIRST_CHILD(this), @@ -2718,7 +2726,6 @@ int quota_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict, int flags, dict_t *xdata) { - data_pair_t *trav = NULL; int32_t op_ret = -1; int32_t op_errno = EINVAL; @@ -2727,7 +2734,7 @@ quota_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, VALIDATE_OR_GOTO (fd, err); GF_IF_INTERNAL_XATTR_GOTO ("trusted.glusterfs.quota*", dict, - trav, op_errno, err); + op_errno, err); STACK_WIND (frame, quota_fsetxattr_cbk, FIRST_CHILD(this), @@ -2805,18 +2812,20 @@ quota_fremovexattr (call_frame_t *frame, xlator_t *this, return 0; } + 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, dict_t *xdata) { - inode_t *root_inode = NULL; - quota_priv_t *priv = NULL; - uint64_t value = 0; - quota_inode_ctx_t *ctx = NULL; - limits_t *limit_node = NULL; - int64_t usage = -1; - int64_t avail = -1; + inode_t *root_inode = NULL; + quota_priv_t *priv = NULL; + uint64_t value = 0; + quota_inode_ctx_t *ctx = NULL; + limits_t *limit_node = NULL; + int64_t usage = -1; + int64_t avail = -1; + int64_t blocks = 0; root_inode = cookie; @@ -2851,7 +2860,12 @@ quota_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, list_for_each_entry (limit_node, &priv->limit_head, limit_list) { /* Notice that this only works for volume-level quota. */ if (strcmp (limit_node->path, "/") == 0) { - buf->f_blocks = limit_node->value / buf->f_bsize; + blocks = limit_node->value / buf->f_bsize; + if (usage > blocks) { + break; + } + + buf->f_blocks = blocks; avail = buf->f_blocks - usage; if (buf->f_bfree > avail) { buf->f_bfree = avail; @@ -2882,8 +2896,11 @@ int32_t quota_statfs (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) { inode_t *root_inode = NULL; + quota_priv_t *priv = NULL; + + priv = this->private; - if (loc->inode) { + if (priv->consider_statfs && loc->inode) { root_inode = loc->inode->table->root; inode_ref(root_inode); STACK_WIND_COOKIE (frame, quota_statfs_cbk, root_inode, @@ -2897,9 +2914,13 @@ quota_statfs (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) * which is exactly what would happen with STACK_UNWIND using * that as a callback. Therefore, use default_statfs_cbk in * this case instead. + * + * Also if the option deem-statfs is not set to "on" don't + * bother calculating quota limit on / in statfs_cbk. */ - gf_log(this->name,GF_LOG_WARNING, - "missing inode, cannot adjust for quota"); + if (priv->consider_statfs) + gf_log(this->name,GF_LOG_WARNING, + "missing inode, cannot adjust for quota"); STACK_WIND (frame, default_statfs_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->statfs, loc, xdata); } @@ -2948,6 +2969,177 @@ err: return 0; } +int32_t +quota_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + int32_t ret = 0; + uint64_t ctx_int = 0; + quota_inode_ctx_t *ctx = NULL; + quota_local_t *local = NULL; + quota_dentry_t *dentry = NULL; + int64_t delta = 0; + + local = frame->local; + + if ((op_ret < 0) || (local == NULL)) { + goto out; + } + + ret = inode_ctx_get (local->loc.inode, this, &ctx_int); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "%s: failed to get the context", local->loc.path); + goto out; + } + + ctx = (quota_inode_ctx_t *)(unsigned long) ctx_int; + + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota context not set in %s (gfid:%s)", + local->loc.path, uuid_utoa (local->loc.inode->gfid)); + goto out; + } + + LOCK (&ctx->lock); + { + ctx->buf = *postbuf; + } + UNLOCK (&ctx->lock); + + list_for_each_entry (dentry, &ctx->parents, next) { + delta = (postbuf->ia_blocks - prebuf->ia_blocks) * 512; + quota_update_size (this, local->loc.inode, + dentry->name, dentry->par, delta); + } + +out: + QUOTA_STACK_UNWIND (fallocate, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + + return 0; +} + +int32_t +quota_fallocate_helper(call_frame_t *frame, xlator_t *this, fd_t *fd, + int32_t mode, off_t offset, size_t len, dict_t *xdata) +{ + quota_local_t *local = NULL; + int32_t op_errno = EINVAL; + + local = frame->local; + if (local == NULL) { + gf_log (this->name, GF_LOG_WARNING, "local is NULL"); + goto unwind; + } + + if (local->op_ret == -1) { + op_errno = local->op_errno; + goto unwind; + } + + STACK_WIND (frame, quota_fallocate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fallocate, fd, mode, offset, len, + xdata); + return 0; + +unwind: + QUOTA_STACK_UNWIND (fallocate, frame, -1, op_errno, NULL, NULL, NULL); + return 0; +} + +int32_t +quota_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t mode, + off_t offset, size_t len, dict_t *xdata) +{ + int32_t ret = -1, op_errno = EINVAL; + int32_t parents = 0; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + quota_priv_t *priv = NULL; + call_stub_t *stub = NULL; + quota_dentry_t *dentry = NULL; + + GF_ASSERT (frame); + GF_VALIDATE_OR_GOTO ("quota", this, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd, unwind); + + local = quota_local_new (); + if (local == NULL) { + goto unwind; + } + + frame->local = local; + local->loc.inode = inode_ref (fd->inode); + + ret = quota_inode_ctx_get (fd->inode, -1, this, NULL, NULL, &ctx, 0); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota context not set in inode (gfid:%s)", + uuid_utoa (fd->inode->gfid)); + goto unwind; + } + + stub = fop_fallocate_stub(frame, quota_fallocate_helper, fd, mode, offset, len, + xdata); + if (stub == NULL) { + op_errno = ENOMEM; + goto unwind; + } + + priv = this->private; + GF_VALIDATE_OR_GOTO (this->name, priv, unwind); + + LOCK (&ctx->lock); + { + list_for_each_entry (dentry, &ctx->parents, next) { + parents++; + } + } + UNLOCK (&ctx->lock); + + /* + * Note that by using len as the delta we're assuming the range from + * offset to offset+len has not already been allocated. This can result + * in ENOSPC errors attempting to allocate an already allocated range. + */ + local->delta = len; + local->stub = stub; + local->link_count = parents; + + list_for_each_entry (dentry, &ctx->parents, next) { + ret = quota_check_limit (frame, fd->inode, this, dentry->name, + dentry->par); + if (ret == -1) { + break; + } + } + + stub = NULL; + + LOCK (&local->lock); + { + local->link_count = 0; + if (local->validate_count == 0) { + stub = local->stub; + local->stub = NULL; + } + } + UNLOCK (&local->lock); + + if (stub != NULL) { + call_resume (stub); + } + + return 0; + +unwind: + QUOTA_STACK_UNWIND (fallocate, frame, -1, op_errno, NULL, NULL, NULL); + return 0; +} + int32_t mem_acct_init (xlator_t *this) @@ -3002,22 +3194,26 @@ quota_forget (xlator_t *this, inode_t *inode) int -quota_parse_limits (quota_priv_t *priv, xlator_t *this, dict_t *xl_options) +quota_parse_limits (quota_priv_t *priv, xlator_t *this, dict_t *xl_options, + struct list_head *old_list) { int32_t ret = -1; char *str = NULL; char *str_val = NULL; - char *path = NULL; + char *path = NULL, *saveptr = NULL; uint64_t value = 0; - limits_t *quota_lim = NULL; + limits_t *quota_lim = NULL, *old = NULL; + char *last_colon= NULL; ret = dict_get_str (xl_options, "limit-set", &str); if (str) { - path = strtok (str, ":"); + path = strtok_r (str, ",", &saveptr); while (path) { - str_val = strtok (NULL, ","); + last_colon = strrchr (path, ':'); + *last_colon = '\0'; + str_val = last_colon + 1; ret = gf_string2bytesize (str_val, &value); if (ret != 0) @@ -3032,20 +3228,40 @@ quota_parse_limits (quota_priv_t *priv, xlator_t *this, dict_t *xl_options) gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, quota_lim->path, quota_lim->value); - list_add_tail ("a_lim->limit_list, - &priv->limit_head); + if (old_list != NULL) { + list_for_each_entry (old, old_list, + limit_list) { + if (strcmp (old->path, quota_lim->path) + == 0) { + uuid_copy (quota_lim->gfid, + old->gfid); + break; + } + } + } + + LOCK (&priv->lock); + { + list_add_tail ("a_lim->limit_list, + &priv->limit_head); + } + UNLOCK (&priv->lock); - path = strtok (NULL, ":"); + path = strtok_r (NULL, ",", &saveptr); } } else { gf_log (this->name, GF_LOG_INFO, "no \"limit-set\" option provided"); } - list_for_each_entry (quota_lim, &priv->limit_head, limit_list) { - gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, quota_lim->path, - quota_lim->value); + LOCK (&priv->lock); + { + list_for_each_entry (quota_lim, &priv->limit_head, limit_list) { + gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, + quota_lim->path, quota_lim->value); + } } + UNLOCK (&priv->lock); ret = 0; err: @@ -3076,15 +3292,18 @@ init (xlator_t *this) INIT_LIST_HEAD (&priv->limit_head); + LOCK_INIT (&priv->lock); + this->private = priv; - ret = quota_parse_limits (priv, this, this->options); + ret = quota_parse_limits (priv, this, this->options, NULL); if (ret) { goto err; } GF_OPTION_INIT ("timeout", priv->timeout, int64, err); + GF_OPTION_INIT ("deem-statfs", priv->consider_statfs, bool, err); this->local_pool = mem_pool_new (quota_local_t, 64); if (!this->local_pool) { @@ -3100,23 +3319,81 @@ err: } +void +__quota_reconfigure_inode_ctx (xlator_t *this, inode_t *inode, limits_t *limit) +{ + int ret = -1; + quota_inode_ctx_t *ctx = NULL; + + GF_VALIDATE_OR_GOTO ("quota", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + GF_VALIDATE_OR_GOTO (this->name, limit, out); + + ret = quota_inode_ctx_get (inode, limit->value, this, NULL, NULL, &ctx, + 1); + if ((ret == -1) || (ctx == NULL)) { + gf_log (this->name, GF_LOG_WARNING, "cannot create quota " + "context in inode(gfid:%s)", + uuid_utoa (inode->gfid)); + goto out; + } + + LOCK (&ctx->lock); + { + ctx->limit = limit->value; + } + UNLOCK (&ctx->lock); + +out: + return; +} + + +void +__quota_reconfigure (xlator_t *this, inode_table_t *itable, limits_t *limit) +{ + inode_t *inode = NULL; + + if ((this == NULL) || (itable == NULL) || (limit == NULL)) { + goto out; + } + + if (!uuid_is_null (limit->gfid)) { + inode = inode_find (itable, limit->gfid); + } else { + inode = inode_resolve (itable, limit->path); + } + + if (inode != NULL) { + __quota_reconfigure_inode_ctx (this, inode, limit); + } + +out: + return; +} + + int reconfigure (xlator_t *this, dict_t *options) { - int32_t ret = -1; - quota_priv_t *priv = NULL; - limits_t *limit = NULL; - limits_t *next = NULL; + int32_t ret = -1; + quota_priv_t *priv = NULL; + limits_t *limit = NULL, *next = NULL, *new = NULL; + struct list_head head = {0, }; + xlator_t *top = NULL; + char found = 0; priv = this->private; - list_for_each_entry_safe (limit, next, &priv->limit_head, limit_list) { - list_del (&limit->limit_list); + INIT_LIST_HEAD (&head); - GF_FREE (limit); + LOCK (&priv->lock); + { + list_splice_init (&priv->limit_head, &head); } + UNLOCK (&priv->lock); - ret = quota_parse_limits (priv, this, options); + ret = quota_parse_limits (priv, this, options, &head); if (ret == -1) { gf_log ("quota", GF_LOG_WARNING, "quota reconfigure failed, " @@ -3124,7 +3401,39 @@ reconfigure (xlator_t *this, dict_t *options) goto out; } + LOCK (&priv->lock); + { + top = ((glusterfs_ctx_t *)this->ctx)->active->top; + GF_ASSERT (top); + + list_for_each_entry (limit, &priv->limit_head, limit_list) { + __quota_reconfigure (this, top->itable, limit); + } + + list_for_each_entry_safe (limit, next, &head, limit_list) { + found = 0; + list_for_each_entry (new, &priv->limit_head, + limit_list) { + if (strcmp (new->path, limit->path) == 0) { + found = 1; + break; + } + } + + if (!found) { + limit->value = -1; + __quota_reconfigure (this, top->itable, limit); + } + + list_del_init (&limit->limit_list); + GF_FREE (limit); + } + } + UNLOCK (&priv->lock); + GF_OPTION_RECONF ("timeout", priv->timeout, options, int64, out); + GF_OPTION_RECONF ("deem-statfs", priv->consider_statfs, options, bool, + out); ret = 0; out: @@ -3166,6 +3475,7 @@ struct xlator_fops fops = { .removexattr = quota_removexattr, .fremovexattr = quota_fremovexattr, .readdirp = quota_readdirp, + .fallocate = quota_fallocate, }; struct xlator_cbks cbks = { @@ -3182,5 +3492,12 @@ struct volume_options options[] = { .description = "quota caches the directory sizes on client. Timeout " "indicates the timeout for the cache to be revalidated." }, + {.key = {"deem-statfs"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "If set to on, it takes quota limits into" + "consideration while estimating fs size. (df command)" + " (Default is off)." + }, {.key = {NULL}} }; |
