diff options
Diffstat (limited to 'xlators/features/quota/src/quota.c')
-rw-r--r-- | xlators/features/quota/src/quota.c | 439 |
1 files changed, 154 insertions, 285 deletions
diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c index 9ad7f2b8..c15eded4 100644 --- a/xlators/features/quota/src/quota.c +++ b/xlators/features/quota/src/quota.c @@ -293,6 +293,13 @@ int32_t quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, char *name, uuid_t par) { +/* This macro is of limited scope, reuse it with care */ +/* crossed: (soft limit && hard timeout) or (soft timeout) */ +#define QUOTA_NEED_VALIDATION(ctx, priv) \ + ((usage > ctx->soft_lim && \ + quota_timeout (&ctx->tv, priv->hard_timeout)) || \ + (quota_timeout (&ctx->tv, priv->soft_timeout))) + int32_t ret = -1; inode_t *_inode = NULL, *parent = NULL; quota_inode_ctx_t *ctx = NULL; @@ -305,6 +312,7 @@ quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, uint64_t value = 0; char just_validated = 0; uuid_t trav_uuid = {0,}; + int64_t usage = 0; GF_VALIDATE_OR_GOTO ("quota", this, out); @@ -341,20 +349,18 @@ quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, } do { - if (ctx != NULL) { + if (ctx != NULL && ctx->hard_lim >= 0) { LOCK (&ctx->lock); { - if (ctx->limit >= 0) { - if (!just_validated - && quota_timeout (&ctx->tv, - priv->timeout)) { + usage = ctx->size + delta; + if (!just_validated) { + if (QUOTA_NEED_VALIDATION (ctx, priv)) { need_validate = 1; - } else if ((ctx->size + delta) - >= ctx->limit) { - local->op_ret = -1; - local->op_errno = EDQUOT; - need_unwind = 1; } + } else if (usage >= ctx->hard_lim) { + local->op_ret = -1; + local->op_errno = EDQUOT; + need_unwind = 1; } } UNLOCK (&ctx->lock); @@ -473,7 +479,7 @@ __quota_init_inode_ctx (inode_t *inode, int64_t limit, xlator_t *this, QUOTA_ALLOC_OR_GOTO (ctx, quota_inode_ctx_t, out); - ctx->limit = limit; + ctx->hard_lim = limit; if (buf) ctx->buf = *buf; @@ -534,43 +540,35 @@ 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) { + struct xattr_limit_set { + int64_t hard_lim; + int64_t soft_lim; + } *read_xattr = NULL; 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; + int64_t soft_lim = 0; + int64_t hard_lim = -1; 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; + GF_VALIDATE_OR_GOTO (this->name, (!op_ret), unwind); + GF_VALIDATE_OR_GOTO (this->name, dict, unwind); + GF_VALIDATE_OR_GOTO (this->name, local, unwind); - if ((op_ret < 0) || (local == NULL) - || (((ctx == NULL) || (ctx->limit == local->limit)) - && (local->limit < 0) && !((IA_ISREG (buf->ia_type)) - || (IA_ISLNK (buf->ia_type))))) { + ret = dict_get_bin (dict, QUOTA_LIMIT_KEY, (void **) &read_xattr); + if (ret && !(IA_ISREG (buf->ia_type) || IA_ISLNK (buf->ia_type))) 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); + if (read_xattr) + hard_lim = read_xattr->hard_lim; - ret = quota_inode_ctx_get (local->loc.inode, local->limit, this, dict, - buf, &ctx, 1); + ret = quota_inode_ctx_get (local->loc.inode, hard_lim, this, dict, buf, + &ctx, 1); if ((ret == -1) || (ctx == NULL)) { gf_log (this->name, GF_LOG_WARNING, "cannot create quota " "context in inode(gfid:%s)", @@ -583,17 +581,10 @@ quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, LOCK (&ctx->lock); { - if (dict != NULL) { - ret = dict_get_bin (dict, QUOTA_SIZE_KEY, - (void **) &size); - if (ret == 0) { - ctx->size = ntoh64 (*size); - gettimeofday (&ctx->tv, NULL); - } - } - - if (local->limit != ctx->limit) { - ctx->limit = local->limit; + if (hard_lim != -1) { + soft_lim = read_xattr->soft_lim? + read_xattr->soft_lim: priv->default_soft_lim; + ctx->soft_lim = (int64_t)ctx->hard_lim * (soft_lim/100); } ctx->buf = *buf; @@ -647,8 +638,6 @@ quota_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr_req) { int32_t ret = -1; - int64_t limit = -1; - limits_t *limit_node = NULL; gf_boolean_t dict_newed = _gf_false; quota_priv_t *priv = NULL; quota_local_t *local = NULL; @@ -658,12 +647,6 @@ quota_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, QUOTA_WIND_IF_DISABLED (priv->quota_on, frame, this, lookup, loc, xattr_req); - list_for_each_entry (limit_node, &priv->limit_head, limit_list) { - if (strcmp (limit_node->path, loc->path) == 0) { - limit = limit_node->value; - } - } - local = quota_local_new (); if (local == NULL) { goto err; @@ -676,12 +659,6 @@ quota_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, frame->local = local; - local->limit = limit; - - if (limit < 0) { - goto wind; - } - if (xattr_req == NULL) { xattr_req = dict_new (); dict_newed = _gf_true; @@ -692,7 +669,10 @@ quota_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, goto err; } -wind: + ret = dict_set_uint64 (xattr_req, QUOTA_LIMIT_KEY, 0); + if (0 > ret) + goto err; + STACK_WIND (frame, quota_lookup_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->lookup, loc, xattr_req); @@ -711,6 +691,59 @@ err: return 0; } +/* Logs if +* i. Usage crossed soft limit +* ii. Usage above soft limit and alert-time timed out +*/ +void +quota_log_usage (xlator_t *this, quota_inode_ctx_t *ctx, inode_t *inode, + int64_t delta) +{ + struct timeval cur_time = {0,}; + char *usage_str = NULL; + char *path = NULL; + int64_t cur_size = 0; + quota_priv_t *priv = NULL; + + priv = this->private; + cur_size = ctx->size + delta; + gettimeofday (&cur_time, NULL); + + /* Usage crossed/reached soft limit? */ + if (DID_REACH_LIMIT (ctx->soft_lim, ctx->size, cur_size)) { + ctx->prev_log = cur_time; + usage_str = gf_uint64_2human_readable (ctx->soft_lim); + inode_path (inode, NULL, &path); + + gf_log (this->name, GF_LOG_ALERT, "Usage crossed soft limit: " + "%s for %s", usage_str, + path? path: uuid_utoa (inode->gfid)); + } + /* Usage crossed/reached hard limit? */ + else if (DID_REACH_LIMIT (ctx->hard_lim, ctx->size, cur_size)) { + ctx->prev_log = cur_time; + usage_str = gf_uint64_2human_readable (ctx->hard_lim); + inode_path (inode, NULL, &path); + + gf_log (this->name, GF_LOG_ALERT, "Usage reached hard limit: " + "%s for %s", usage_str, + path? path: uuid_utoa (inode->gfid)); + } + /* Usage is above soft limit and 'alert-time' timed out */ + else if (cur_size > ctx->soft_lim && + quota_timeout (&ctx->prev_log, priv->log_timeout)) { + ctx->prev_log = cur_time; + usage_str = gf_uint64_2human_readable (cur_size); + inode_path (inode, NULL, &path); + + gf_log (this->name, GF_LOG_ALERT, "Usage %s %s limit for %s ", + usage_str, (cur_size < ctx->hard_lim)? + "is above soft": "has reached hard", + path? path: uuid_utoa (inode->gfid)); + } + if (usage_str) + GF_FREE (usage_str); +} void quota_update_size (xlator_t *this, inode_t *inode, char *name, uuid_t par, @@ -735,7 +768,9 @@ quota_update_size (xlator_t *this, inode_t *inode, char *name, uuid_t par, } do { - if ((ctx != NULL) && (ctx->limit >= 0)) { + if ((ctx != NULL) && (ctx->hard_lim >= 0)) { + quota_log_usage (this, ctx, _inode, delta); + LOCK (&ctx->lock); { ctx->size += delta; @@ -2029,7 +2064,7 @@ quota_send_dir_limit_to_cli (call_frame_t *frame, xlator_t *this, goto out; ctx = (quota_inode_ctx_t *)(unsigned long)value; - snprintf (dir_limit, 1024, "%"PRId64",%"PRId64, ctx->size, ctx->limit); + snprintf (dir_limit, 1024, "%"PRId64",%"PRId64, ctx->size, ctx->hard_lim); dict = dict_new (); if (dict == NULL) { @@ -2900,10 +2935,8 @@ quota_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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; int64_t blocks = 0; @@ -2937,33 +2970,26 @@ quota_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, ctx = (quota_inode_ctx_t *)(unsigned long)value; usage = (ctx->size) / buf->f_bsize; - priv = this->private; - 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) { - blocks = limit_node->value / buf->f_bsize; - if (usage > blocks) { - break; - } + /* Notice that this only works for volume-level quota. */ + blocks = ctx->hard_lim / buf->f_bsize; + if (usage > blocks) + goto unwind; - buf->f_blocks = blocks; - avail = buf->f_blocks - usage; - if (buf->f_bfree > avail) { - buf->f_bfree = avail; - } - /* - * We have to assume that the total assigned quota - * won't cause us to dip into the reserved space, - * because dealing with the overcommitted cases is - * just too hairy (especially when different bricks - * might be using different reserved percentages and - * such). - */ - buf->f_bavail = buf->f_bfree; - break; - } - } + buf->f_blocks = blocks; + avail = buf->f_blocks - usage; + if (buf->f_bfree > avail) { + buf->f_bfree = avail; + } + /* + * We have to assume that the total assigned quota + * won't cause us to dip into the reserved space, + * because dealing with the overcommitted cases is + * just too hairy (especially when different bricks + * might be using different reserved percentages and + * such). + */ + buf->f_bavail = buf->f_bfree; unwind: if (root_inode) { @@ -3288,82 +3314,6 @@ quota_forget (xlator_t *this, inode_t *inode) } -int -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, *saveptr = NULL; - uint64_t value = 0; - limits_t *quota_lim = NULL, *old = NULL; - char *last_colon= NULL; - - ret = dict_get_str (xl_options, "limit-set", &str); - - if (str) { - path = strtok_r (str, ",", &saveptr); - - while (path) { - last_colon = strrchr (path, ':'); - *last_colon = '\0'; - str_val = last_colon + 1; - - ret = gf_string2bytesize (str_val, &value); - if (ret != 0) - goto err; - - QUOTA_ALLOC_OR_GOTO (quota_lim, limits_t, err); - - quota_lim->path = path; - - quota_lim->value = value; - - gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, - quota_lim->path, quota_lim->value); - - 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_r (NULL, ",", &saveptr); - } - } else { - gf_log (this->name, GF_LOG_INFO, - "no \"limit-set\" option provided"); - } - - 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: - return ret; -} - - int32_t init (xlator_t *this) { @@ -3385,18 +3335,13 @@ init (xlator_t *this) QUOTA_ALLOC_OR_GOTO (priv, quota_priv_t, err); - INIT_LIST_HEAD (&priv->limit_head); - - LOCK_INIT (&priv->lock); this->private = priv; - ret = quota_parse_limits (priv, this, this->options, NULL); - - if (ret) { - goto err; - } - - GF_OPTION_INIT ("timeout", priv->timeout, int64, err); + GF_OPTION_INIT ("hard-timeout", priv->hard_timeout, time, err); + GF_OPTION_INIT ("soft-timeout", priv->soft_timeout, time, err); + GF_OPTION_INIT ("alert-time", priv->log_timeout, time, err); + GF_OPTION_INIT ("default-soft-limit", priv->default_soft_lim, percent, + err); GF_OPTION_INIT ("deem-statfs", priv->consider_statfs, bool, err); GF_OPTION_INIT ("server-quota", priv->quota_on, bool, err); @@ -3414,120 +3359,23 @@ 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, *next = NULL, *new = NULL; - struct list_head head = {0, }; - xlator_t *top = NULL; - char found = 0; priv = this->private; GF_VALIDATE_OR_GOTO (this->name, priv, out); - INIT_LIST_HEAD (&head); - - LOCK (&priv->lock); - { - list_splice_init (&priv->limit_head, &head); - } - UNLOCK (&priv->lock); - - ret = quota_parse_limits (priv, this, options, &head); - if (ret == -1) { - gf_log ("quota", GF_LOG_WARNING, - "quota reconfigure failed, " - "new changes will not take effect"); - 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 ("hard-timeout", priv->hard_timeout, options, time, + out); + GF_OPTION_RECONF ("soft-timeout", priv->soft_timeout, options, time, + out); + GF_OPTION_RECONF ("alert-time", priv->log_timeout, options, time, out); + GF_OPTION_RECONF ("default-soft-limit", priv->default_soft_lim, + options, percent, out); GF_OPTION_RECONF ("deem-statfs", priv->consider_statfs, options, bool, out); GF_OPTION_RECONF ("server-quota", priv->quota_on, options, bool, out); @@ -3579,14 +3427,35 @@ struct xlator_cbks cbks = { }; struct volume_options options[] = { - {.key = {"limit-set"}}, - {.key = {"timeout"}, - .type = GF_OPTION_TYPE_SIZET, + {.key = {"hard-timeout"}, + .type = GF_OPTION_TYPE_TIME, .min = 0, - .max = 60, + .max = LONG_MAX, .default_value = "0", - .description = "quota caches the directory sizes on client. Timeout " - "indicates the timeout for the cache to be revalidated." + .description = "" + }, + {.key = {"soft-timeout"}, + .type = GF_OPTION_TYPE_TIME, + .min = 0, + .max = LONG_MAX, + .default_value = "10", + .description = "" + }, + {.key = {"alert-time"}, + .type = GF_OPTION_TYPE_TIME, + .min = 0, + .max = LONG_MAX, + .default_value = "604800", + .description = "" + }, + {.key = {"default-soft-limit"}, + .type = GF_OPTION_TYPE_PERCENT, + .min = 0, + .max = 100, + .default_value = "90%", + .description = "Takes this if individual paths are not configured " + "with soft limits. Individual limits overrides this " + "option." }, {.key = {"deem-statfs"}, .type = GF_OPTION_TYPE_BOOL, |