diff options
Diffstat (limited to 'xlators/features/leases/src/leases-internal.c')
| -rw-r--r-- | xlators/features/leases/src/leases-internal.c | 1333 | 
1 files changed, 1333 insertions, 0 deletions
diff --git a/xlators/features/leases/src/leases-internal.c b/xlators/features/leases/src/leases-internal.c new file mode 100644 index 00000000000..8ed0abcae82 --- /dev/null +++ b/xlators/features/leases/src/leases-internal.c @@ -0,0 +1,1333 @@ +/* +   Copyright (c) 2015-2016 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. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "leases.h" + + +/* Mutex locks used in this xlator and their order of acquisition: + * Check lease conflict: + *         lease_ctx lock + *                 add_timer => internal timer locks + *         lease_ctx unlock + * + * Add/remove lease: + *         lease_ctx lock + *                 add_timer => internal timer locks + *                 OR + *                 priv lock => Adding/removing to/from the cleanup client list + *                 priv unlock + *         lease_ctx unlock + * + * Timer thread: + *         Timer internal lock + *                 priv lock => By timer handler + *                 priv unlock + *         Timer internal unlock + * + * Expired recall cleanup thread: + *         priv lock + *                 priv condwait + *         priv unlock + *         lease_ctx lock + *                 priv lock + *                 priv unlock + *         lease_ctx unlock + */ + +/* + * Check if lease_lk is enabled + * Return Value: + * _gf_true  - lease lock option enabled + * _gf_false - lease lock option disabled + */ +gf_boolean_t +is_leases_enabled (xlator_t *this) +{ +        leases_private_t    *priv       = NULL; +        gf_boolean_t         is_enabled = _gf_false; + +        GF_VALIDATE_OR_GOTO ("leases", this, out); + +        if (this->private) { +                priv = (leases_private_t *)this->private; +                is_enabled = priv->leases_enabled; +        } +out: +        return is_enabled; +} + + +/* + * Get the recall_leaselk_timeout + * Return Value: + * timeout value(in seconds) set as an option to this xlator. + * -1 error case + */ +int32_t +get_recall_lease_timeout (xlator_t *this) +{ +        leases_private_t    *priv       = NULL; +        int32_t              timeout    = -1; + +        GF_VALIDATE_OR_GOTO ("leases", this, out); + +        if (this->private) { +                priv = (leases_private_t *)this->private; +                timeout = priv->recall_lease_timeout; +        } +out: +        return timeout; +} + + +static void +__dump_leases_info (xlator_t *this, lease_inode_ctx_t *lease_ctx) +{ +        lease_id_entry_t   *lease_entry     = NULL; +        lease_id_entry_t   *tmp             = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", this, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); + +        gf_msg_debug (this->name, 0, "Lease held on this inode, lease_type: %d," +                      " lease_cnt:%"PRIu64", RD lease:%d, RW lease:%d, " +                      "openfd cnt:%"PRIu64, lease_ctx->lease_type, +                      lease_ctx->lease_cnt, +                      lease_ctx->lease_type_cnt[GF_RD_LEASE], +                      lease_ctx->lease_type_cnt[GF_RW_LEASE], +                      lease_ctx->openfd_cnt); + +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { +                gf_msg_debug (this->name, 0, "Leases held by client: %s, lease " +                              "ID:%s, RD lease:%d, RW lease:%d, lease_type: %d, " +                              "lease_cnt:%"PRIu64, lease_entry->client_uid, +                              lease_entry->lease_id, +                              lease_entry->lease_type_cnt[GF_RD_LEASE], +                              lease_entry->lease_type_cnt[GF_RW_LEASE], +                              lease_entry->lease_type, lease_entry->lease_cnt); +        } +out: +        return; +} + + +static int +__lease_ctx_set (inode_t *inode, xlator_t *this) +{ +        lease_inode_ctx_t   *inode_ctx    = NULL; +        int                  ret          = -1; +        uint64_t             ctx          = 0; + +        GF_VALIDATE_OR_GOTO ("leases", inode, out); +        GF_VALIDATE_OR_GOTO ("leases", this, out); + +        ret = __inode_ctx_get (inode, this, &ctx); +        if (!ret) { +                gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_INVAL_INODE_CTX, +                        "inode_ctx_get failed"); +                goto out; +        } + +        inode_ctx = GF_CALLOC (1, sizeof (*inode_ctx), +                               gf_leases_mt_lease_inode_ctx_t); +        GF_CHECK_ALLOC (inode_ctx, ret, out); + +        pthread_mutex_init (&inode_ctx->lock, NULL); +        INIT_LIST_HEAD (&inode_ctx->lease_id_list); +        INIT_LIST_HEAD (&inode_ctx->blocked_list); + +        inode_ctx->lease_cnt = 0; + +        ret = __inode_ctx_set (inode, this, (uint64_t *) inode_ctx); +        if (ret) { +                GF_FREE (inode_ctx); +                gf_msg (this->name, GF_LOG_INFO, 0, LEASE_MSG_INVAL_INODE_CTX, +                        "failed to set inode ctx (%p)", inode); +        } +out: +        return ret; +} + + +static lease_inode_ctx_t * +__lease_ctx_get (inode_t *inode, xlator_t *this) +{ +        lease_inode_ctx_t *inode_ctx    = NULL; +        uint64_t           ctx          = 0; +        int                ret          = 0; + +        GF_VALIDATE_OR_GOTO ("leases", inode, out); +        GF_VALIDATE_OR_GOTO ("leases", this, out); + +        ret = __inode_ctx_get (inode, this, &ctx); +        if (ret < 0) { +                ret = __lease_ctx_set (inode, this); +                if (ret < 0) +                        goto out; + +                ret = __inode_ctx_get (inode, this, &ctx); +                if (ret < 0) { +                        gf_msg (this->name, GF_LOG_WARNING, 0, LEASE_MSG_INVAL_INODE_CTX, +                                "failed to get inode ctx (%p)", inode); +                        goto out; +                } +        } + +        inode_ctx = (lease_inode_ctx_t *)(long) ctx; +out: +        return inode_ctx; +} + + +lease_inode_ctx_t * +lease_ctx_get (inode_t *inode, xlator_t *this) +{ +        lease_inode_ctx_t *inode_ctx = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", inode, out); +        GF_VALIDATE_OR_GOTO ("leases", this, out); + +        LOCK (&inode->lock); +        { +                inode_ctx = __lease_ctx_get (inode, this); +        } +        UNLOCK (&inode->lock); +out: +        return inode_ctx; +} + + +static lease_id_entry_t * +new_lease_id_entry (call_frame_t *frame, const char *lease_id) +{ +        lease_id_entry_t *lease_entry = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", frame, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_id, out); + +        lease_entry = GF_CALLOC (1, sizeof (*lease_entry), +                                  gf_leases_mt_lease_id_entry_t); +        if (!lease_entry) { +                gf_msg (frame->this->name, GF_LOG_ERROR, ENOMEM, LEASE_MSG_NO_MEM, +                        "Memory allocation for lease_entry failed"); +                return NULL; +        } + +        INIT_LIST_HEAD (&lease_entry->lease_id_list); +        lease_entry->lease_type = NONE; +        lease_entry->lease_cnt = 0; +        lease_entry->recall_time = +                        get_recall_lease_timeout (frame->this); +        lease_entry->client_uid = gf_strdup (frame->root->client->client_uid); +        if (!lease_entry->client_uid) { +                gf_msg (frame->this->name, GF_LOG_ERROR, ENOMEM, LEASE_MSG_NO_MEM, +                        "Memory allocation for client_uid failed"); +                GF_FREE (lease_entry); +                lease_entry = NULL; +                goto out; +        } + +        memcpy (lease_entry->lease_id, lease_id, LEASE_ID_SIZE); +out: +        return lease_entry; +} + + +static void +__destroy_lease_id_entry (lease_id_entry_t *lease_entry) +{ +        GF_VALIDATE_OR_GOTO ("leases", lease_entry, out); + +        list_del_init (&lease_entry->lease_id_list); +        GF_FREE (lease_entry->client_uid); +        GF_FREE (lease_entry); +out: +        return; +} + + +static inline gf_boolean_t +__is_same_lease_id (const char *k1, const char *k2) +{ +        if (memcmp(k1, k2, LEASE_ID_SIZE) == 0) +                return _gf_true; + +        return _gf_false; +} + + +/* Checks if there are any leases, other than the leases taken + * by the given lease_id + */ +static gf_boolean_t +__another_lease_found (lease_inode_ctx_t *lease_ctx, const char *lease_id) +{ +        lease_id_entry_t   *lease_entry     = NULL; +        lease_id_entry_t   *tmp             = NULL; +        gf_boolean_t        found_lease     = _gf_false; + +        GF_VALIDATE_OR_GOTO ("leases", lease_id, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); + +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { + +                if (!__is_same_lease_id (lease_id, lease_entry->lease_id)) { +                        if (lease_entry->lease_cnt > 0) { +                                found_lease = _gf_true; +                                break; +                        } +                } +        } +out: +        return found_lease; +} + + +/* Returns the lease_id_entry for a given lease_id and a given inode. + * Return values: + * NULL - If no client entry found + * lease_id_entry_t* - a pointer to the client entry if found + */ +static lease_id_entry_t * +__get_lease_id_entry (lease_inode_ctx_t *lease_ctx, const char *lease_id) +{ +        lease_id_entry_t   *lease_entry    = NULL; +        lease_id_entry_t   *tmp            = NULL; +        lease_id_entry_t   *found          = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", lease_id, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); + +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { + +                if (__is_same_lease_id (lease_id, lease_entry->lease_id)) { +                        found = lease_entry; +                        gf_msg_debug ("leases", 0, "lease ID entry found " +                                      "Client UID:%s, lease id:%s", +                                      lease_entry->client_uid, +                                      leaseid_utoa (lease_entry->lease_id)); +                        break; +                } +        } +out: +        return found; +} + + +/* Returns the lease_id_entry for a given lease_id and a given inode, + * if none found creates one. + * Return values: + * lease_id_entry_t* - a pointer to the client entry + */ +static lease_id_entry_t * +__get_or_new_lease_entry (call_frame_t *frame, const char *lease_id, +                           lease_inode_ctx_t *lease_ctx) +{ +        lease_id_entry_t *lease_entry    = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", frame, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_id, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); + +        lease_entry = __get_lease_id_entry (lease_ctx, lease_id); +        if (!lease_entry) { /* create one */ +                lease_entry = new_lease_id_entry (frame, lease_id); +                if (!lease_entry) +                        goto out; + +                list_add_tail (&lease_entry->lease_id_list, +                               &lease_ctx->lease_id_list); + +                gf_msg_debug (frame->this->name, 0, "lease ID entry added," +                              " Client UID:%s, lease id:%s", +                              lease_entry->client_uid, +                              leaseid_utoa (lease_entry->lease_id)); +        } +out: +        return lease_entry; +} + + +static lease_inode_t * +new_lease_inode (inode_t *inode) +{ +        lease_inode_t *l_inode = NULL; + +        l_inode = GF_CALLOC (1, sizeof (*l_inode), gf_leases_mt_lease_inode_t); +        if (!l_inode) +                goto out; + +        INIT_LIST_HEAD (&l_inode->list); +        l_inode->inode = inode_ref (inode); +out: +        return l_inode; +} + + +static void +__destroy_lease_inode (lease_inode_t *l_inode) +{ +        list_del_init (&l_inode->list); +        inode_unref (l_inode->inode); +        GF_FREE (l_inode); +} + + +static lease_client_t * +new_lease_client (const char *client_uid) +{ +        lease_client_t  *clnt = NULL; + +        clnt = GF_CALLOC (1, sizeof (*clnt), gf_leases_mt_lease_client_t); +        if (!clnt) +                goto out; + +        INIT_LIST_HEAD (&clnt->client_list); +        INIT_LIST_HEAD (&clnt->inode_list); +        clnt->client_uid = gf_strdup (client_uid); +out: +        return clnt; +} + + +static void +__destroy_lease_client (lease_client_t *clnt) +{ +         list_del_init (&clnt->inode_list); +         list_del_init (&clnt->client_list); +         GF_FREE (clnt); + +         return; +} + + +static lease_client_t * +__get_lease_client (xlator_t *this, leases_private_t *priv, +                    const char *client_uid) +{ +        lease_client_t  *clnt  = NULL; +        lease_client_t  *tmp   = NULL; +        lease_client_t  *found = NULL; + +        list_for_each_entry_safe (clnt, tmp, &priv->client_list, client_list) { +                if ((strcmp (clnt->client_uid, client_uid) == 0)) { +                        found = clnt; +                        gf_msg_debug (this->name, 0, "Client:%s already found " +                                      "in the cleanup list", client_uid); +                        break; +                } +        } +        return found; +} + + +static lease_client_t * +__get_or_new_lease_client (xlator_t *this, leases_private_t *priv, +                           const char *client_uid) +{ +        lease_client_t  *found = NULL; + +        found = __get_lease_client (this, priv, client_uid); +        if (!found) { +                found = new_lease_client (client_uid); +                if (!found) +                        goto out; +                list_add_tail (&found->client_list, &priv->client_list); +                gf_msg_debug (this->name, 0, "Adding a new client:%s entry " +                              "to the cleanup list", client_uid); +        } +out: +        return found; +} + + +static int +add_inode_to_client_list (xlator_t *this, inode_t *inode, const char *client_uid) +{ +        int               ret         = 0; +        leases_private_t *priv        = NULL; +        lease_client_t   *clnt        = NULL; +        lease_inode_t    *lease_inode = NULL; + +        priv = this->private; +        pthread_mutex_lock (&priv->mutex); +        { +                clnt = __get_or_new_lease_client (this, priv, client_uid); +                GF_CHECK_ALLOC (clnt, ret, out); + +                lease_inode = new_lease_inode (inode); +                GF_CHECK_ALLOC (lease_inode, ret, out); + +                list_add_tail (&clnt->inode_list, &lease_inode->list); +                gf_msg_debug (this->name, 0, +                              "Added a new inode:%p to the client(%s) " +                              "cleanup list, gfid(%s)", inode, client_uid, +                              uuid_utoa (inode->gfid)); +        } +        pthread_mutex_unlock (&priv->mutex); +out: +        return ret; +} + + +/* Add lease entry to the corresponding client entry. + * Return values: + * 0 Success + * -1 Failure + */ +static int +__add_lease (call_frame_t *frame, inode_t *inode, lease_inode_ctx_t *lease_ctx, +             const char *client_uid, struct gf_lease *lease) +{ +        lease_id_entry_t  *lease_entry  = NULL; +        int                ret          = -1; + +        GF_VALIDATE_OR_GOTO ("leases", frame, out); +        GF_VALIDATE_OR_GOTO ("leases", client_uid, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); +        GF_VALIDATE_OR_GOTO ("leases", inode, out); +        GF_VALIDATE_OR_GOTO ("leases", lease, out); + +        gf_msg_trace (frame->this->name, 0, +                      "Granting lease lock to client %s with lease id %s" +                      " on gfid(%s)", client_uid, leaseid_utoa (lease->lease_id), +                      uuid_utoa (inode->gfid)); + +        lease_entry = __get_or_new_lease_entry (frame, lease->lease_id, lease_ctx); +        if (!lease_entry) { +                errno = ENOMEM; +                goto out; +        } + +        lease_entry->lease_type_cnt[lease->lease_type]++; +        lease_entry->lease_cnt++; +        lease_entry->lease_type |= lease->lease_type; +        /* If this is the first lease taken by the client on the file, then +         * add this inode/file to the client disconnect cleanup list +         */ +        if (lease_entry->lease_cnt == 1) { +                add_inode_to_client_list (frame->this, inode, client_uid); +        } + +        lease_ctx->lease_cnt++; +        lease_ctx->lease_type_cnt[lease->lease_type]++; +        lease_ctx->lease_type |= lease->lease_type; + +        /* Take a ref for the first lock taken on this inode. Corresponding +         * unref when all the leases are unlocked or during DISCONNECT +         * Ref is required because the inode on which lease is acquired should +         * not be deleted when lru cleanup kicks in*/ +        if (lease_ctx->lease_cnt == 1) { +                lease_ctx->inode = inode_ref (inode); +        } + +        ret = 0; +out: +        return ret; +} + + +static gf_boolean_t +__is_clnt_lease_none (const char *client_uid, lease_inode_ctx_t *lease_ctx) +{ +        gf_boolean_t      lease_none  = _gf_true; +        lease_id_entry_t *lease_entry = NULL; +        lease_id_entry_t *tmp         = NULL; + +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { +                if ((strcmp (client_uid, lease_entry->client_uid) == 0) +                    && (lease_entry->lease_cnt != 0)) { +                        lease_none = _gf_false; +                        break; +                } +        } + +        return lease_none; +} + +static int +__remove_inode_from_clnt_list (xlator_t *this, lease_client_t *clnt, +                               inode_t *inode) +{ +        int               ret     = -1; +        lease_inode_t    *l_inode = NULL; +        lease_inode_t    *tmp1    = NULL; + +        list_for_each_entry_safe (l_inode, tmp1, +                                  &clnt->inode_list, +                                  list) { +                if (l_inode->inode == inode) { +                        __destroy_lease_inode (l_inode); +                        gf_msg_debug (this->name, 0, +                                      "Removed the inode from the client cleanup list"); +                        ret = 0; +                } +        } +        /* TODO: Remove the client entry from the cleanup list */ + +        return ret; +} + + +static int +remove_from_clnt_list (xlator_t *this, const char *client_uid, inode_t *inode) +{ +        leases_private_t *priv    = NULL; +        int               ret     = -1; +        lease_client_t   *clnt    = NULL; + +        priv = this->private; +        if (!priv) +                goto out; + +        pthread_mutex_lock (&priv->mutex); +        { +                clnt = __get_lease_client (this, priv, client_uid); +                if (!clnt) { +                        gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_CLNT_NOTFOUND, +                                "There is no client entry found in the cleanup list"); +                        pthread_mutex_unlock (&priv->mutex); +                        goto out; +                } +                ret = __remove_inode_from_clnt_list (this, clnt, inode); +                if (ret) { +                        gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_INODE_NOTFOUND, +                                "There is no inode entry found in the cleanup list"); +                } +        } +        pthread_mutex_unlock (&priv->mutex); +out: +        return ret; +} + + +/* Remove lease entry in the corresponding client entry. + */ +static int +__remove_lease (xlator_t *this, inode_t *inode, lease_inode_ctx_t *lease_ctx, +                const char *client_uid, struct gf_lease *lease) +{ +        lease_id_entry_t    *lease_entry     = NULL; +        int                  ret             = 0; +        int32_t              lease_type      = 0; +        leases_private_t    *priv            = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); +        GF_VALIDATE_OR_GOTO ("leases", lease, out); + +        priv = this->private; + +        gf_msg_trace (this->name, 0, "Removing lease entry for client: %s, " +                      "lease type:%d, lease id:%s", client_uid, lease->lease_type, +                      leaseid_utoa (lease->lease_id)); + +        lease_entry = __get_lease_id_entry (lease_ctx, lease->lease_id); +        if (!lease_entry) { +                gf_msg (this->name, GF_LOG_INFO, 0, LEASE_MSG_INVAL_UNLK_LEASE, +                        "Got unlock lease request from client:%s, but has no " +                        "corresponding lock", client_uid); +                ret = -EINVAL; +                errno = EINVAL; +                goto out; +        } + +        lease_type = lease->lease_type; +        lease_entry->lease_type_cnt[lease_type]--; +        lease_entry->lease_cnt--; + +        lease_ctx->lease_type_cnt[lease_type]--; +        lease_ctx->lease_cnt--; + +        if (lease_entry->lease_type_cnt[lease_type] == 0) +                lease_entry->lease_type = lease_entry->lease_type & (~lease_type); + +        if (lease_ctx->lease_type_cnt[lease_type] == 0) +                lease_ctx->lease_type = lease_ctx->lease_type & (~lease_type); + +        if (lease_entry->lease_cnt == 0) { +                if (__is_clnt_lease_none (client_uid, lease_ctx)) { +                        gf_msg_debug (this->name, 0, "Client(%s) has no leases" +                                      " on gfid (%s), hence removing the inode" +                                      " from the client cleanup list", +                                      client_uid, uuid_utoa (inode->gfid)); +                        remove_from_clnt_list (this, client_uid, lease_ctx->inode); +                } +                __destroy_lease_id_entry (lease_entry); +        } + +        if (lease_ctx->lease_cnt == 0 && lease_ctx->timer) { +                ret = gf_tw_del_timer (priv->timer_wheel, lease_ctx->timer); +                lease_ctx->recall_in_progress = _gf_false; +        } +out: +        return ret; +} + + +static gf_boolean_t +__is_lease_grantable (xlator_t *this, lease_inode_ctx_t *lease_ctx, +                      struct gf_lease *lease, inode_t *inode) +{ +        uint32_t        fd_count     = 0; +        int32_t         flags        = 0; +        fd_t           *iter_fd      = NULL; +        gf_boolean_t    grant        = _gf_false; +        int             ret          = 0; +        lease_fd_ctx_t *fd_ctx       = NULL; +        uint64_t        ctx          = 0; + +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); +        GF_VALIDATE_OR_GOTO ("leases", lease, out); +        GF_VALIDATE_OR_GOTO ("leases", inode, out); + +        if (lease_ctx->recall_in_progress) { +                gf_msg_debug (this->name, 0, "Recall in progress, hence " +                              "failing the lease request"); +                grant = _gf_false; +                goto out; +        } + +        LOCK (&inode->lock); +        { +                list_for_each_entry (iter_fd, &inode->fd_list, inode_list) { +                        ret = fd_ctx_get (iter_fd, this, &ctx); +                        if (ret < 0) { +                                grant = _gf_false; +                                UNLOCK (&inode->lock); +                                gf_msg (this->name, GF_LOG_ERROR, 0, +                                        LEASE_MSG_INVAL_FD_CTX, +                                        "Unable to get fd ctx"); +                                goto out; +                        } +                        fd_ctx = (lease_fd_ctx_t *)(long) ctx; + +                        /* Check for open fd conflict, note that open fds from +                         * the same lease id is not checked for conflict, as it is +                         * lease id based lease. +                         */ +                        if (!__is_same_lease_id (fd_ctx->lease_id, lease->lease_id)) { +                                fd_count++; +                                flags |= iter_fd->flags; +                        } +                } +        } +        UNLOCK (&inode->lock); + +        gf_msg_debug (this->name, 0, "open fd count:%d flags:%d", +                      fd_count, flags); + +        __dump_leases_info (this, lease_ctx); + +        switch (lease->lease_type) { +        case GF_RD_LEASE: +                /* check open fd conflict */ +                if ((fd_count > 0) && ((flags & O_WRONLY) || (flags & O_RDWR))) { +                        grant = _gf_false; +                        break; +                } + +                /* check for conflict with existing leases */ +                if (lease_ctx->lease_type == NONE || lease_ctx->lease_type == GF_RD_LEASE) +                        grant = _gf_true; +                else +                        grant = _gf_false; +                break; + +        case GF_RW_LEASE: +                /* check open fd conflict; conflict if there are any fds open +                 * other than the client on which the lease is requested. */ +                if (fd_count > 0) { +                        grant = _gf_false; +                        break; +                } + +                /* check existing lease conflict */ +                if (lease_ctx->lease_type == NONE || +                    !(__another_lease_found (lease_ctx, lease->lease_id))) +                        grant = _gf_true; +                else +                        grant = _gf_false; +                break; + +        default: +                gf_msg (this->name, GF_LOG_ERROR, EINVAL, LEASE_MSG_INVAL_LEASE_TYPE, +                        "Invalid lease type specified"); +                break; +        } +out: +        return grant; +} + + +static void +do_blocked_fops (xlator_t *this, lease_inode_ctx_t *lease_ctx) +{ +        struct list_head   wind_list; +        fop_stub_t        *blk_fop  = NULL; +        fop_stub_t        *tmp      = NULL; + +        INIT_LIST_HEAD (&wind_list); + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                list_for_each_entry_safe (blk_fop, tmp, +                                          &lease_ctx->blocked_list, list) { +                        list_del_init (&blk_fop->list); +                        list_add_tail (&blk_fop->list, &wind_list); +                } +        } +        pthread_mutex_unlock (&lease_ctx->lock); + +        gf_msg_trace (this->name, 0, "Executing the blocked stubs on gfid(%s)", +                      uuid_utoa (lease_ctx->inode->gfid)); + +        list_for_each_entry_safe (blk_fop, tmp, &wind_list, list) { +                list_del_init (&blk_fop->list); +                gf_msg_trace (this->name, 0, "Executing fop:%d", blk_fop->stub->fop); +                call_resume (blk_fop->stub); +                GF_FREE (blk_fop); +        } + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                lease_ctx->lease_type = NONE; +                inode_unref (lease_ctx->inode); +                lease_ctx->inode = NULL; +        } +        pthread_mutex_unlock (&lease_ctx->lock); + +        return; +} + + +void +recall_lease_timer_handler (struct gf_tw_timer_list *timer, +                            void *data, unsigned long calltime) +{ +        inode_t          *inode       = NULL; +        lease_inode_t    *lease_inode = NULL; +        leases_private_t *priv        = NULL; + +        priv = THIS->private; +        inode = (inode_t *)data; +        pthread_mutex_lock (&priv->mutex); +        { +                lease_inode = new_lease_inode (inode); +                if (!lease_inode) { +                        errno = ENOMEM; +                        goto out; +                } +                list_add_tail (&lease_inode->list, &priv->recall_list); +                pthread_cond_broadcast (&priv->cond); +        } +out: +        pthread_mutex_unlock (&priv->mutex); + +        GF_FREE (timer); +} + + +static void +__recall_lease (xlator_t *this, lease_inode_ctx_t *lease_ctx) +{ +        lease_id_entry_t                  *lease_entry   = NULL; +        lease_id_entry_t                  *tmp           = NULL; +        struct gf_upcall                   up_req        = {0,}; +        struct gf_upcall_recall_lease      recall_req    = {0,}; +        int                                notify_ret    = -1; +        struct gf_tw_timer_list           *timer         = NULL; +        leases_private_t                  *priv          = NULL; + +        if (lease_ctx->recall_in_progress) { +                gf_msg_debug (this->name, 0, "Lease recall is already in " +                              "progress, hence not sending another recall"); +                goto out; +        } + +        priv = this->private; +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { +                gf_uuid_copy (up_req.gfid, lease_ctx->inode->gfid); +                up_req.client_uid = lease_entry->client_uid; +                up_req.event_type = GF_UPCALL_RECALL_LEASE; +                up_req.data = &recall_req; + +                notify_ret = this->notify (this, GF_EVENT_UPCALL, &up_req); +                if (notify_ret < 0) { +                        gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_RECALL_FAIL, +                                "Recall notification to client: %s failed", +                                lease_entry->client_uid); +                        /* Do not return from here, continue registering the timer, +                           this is required mostly o keep replicas in sync*/ +                } else { +                        gf_msg_debug (this->name, 0, "Recall lease (all)" +                                      "notification sent to client %s", +                                      lease_entry->client_uid); +                } + +                lease_ctx->recall_in_progress = _gf_true; +                lease_entry->recall_time = time (NULL); +        } +        timer = GF_CALLOC (1, sizeof (*timer), +                           gf_common_mt_tw_timer_list); +        if (!timer) { +                goto out; +        } +        INIT_LIST_HEAD (&timer->entry); +        timer->data = inode_ref (lease_ctx->inode); +        timer->expires = get_recall_lease_timeout (this); +        timer->function = recall_lease_timer_handler; +        lease_ctx->timer = timer; +        gf_tw_add_timer (priv->timer_wheel, timer); +        gf_msg_trace (this->name, 0, "Registering timer " "%p, after " +                      "sending recall", timer); +out: +        return; +} + + +/* ret = 0; STACK_UNWIND Success + * ret = -1; STACK_UNWIND failure + */ +int +process_lease_req (call_frame_t *frame, xlator_t *this, +                   inode_t *inode, struct gf_lease *lease) +{ +        int                 ret             = 0; +        char               *client_uid      = NULL; +        lease_inode_ctx_t  *lease_ctx       = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", frame, out); +        GF_VALIDATE_OR_GOTO ("leases", this, out); +        GF_VALIDATE_OR_GOTO ("leases", inode, out); +        GF_VALIDATE_OR_GOTO ("leases", lease, out); + +        client_uid = frame->root->client->client_uid; + +        if (!is_valid_lease_id (lease->lease_id)) { +                gf_msg (this->name, GF_LOG_ERROR, EINVAL, +                        LEASE_MSG_INVAL_LEASE_ID, "Invalid lease id, from" +                        "client:%s", client_uid); +                ret = -EINVAL; +                errno = EINVAL; +                goto out; +        } + +        lease_ctx = lease_ctx_get (inode, this); +        if (!lease_ctx) { +                gf_msg (this->name, GF_LOG_WARNING, ENOMEM, +                        LEASE_MSG_NO_MEM, "Unable to create/get inode ctx, " +                        "inode:%p", inode); +                ret = -ENOMEM; +                errno = ENOMEM; +                goto out; +        } + +        gf_msg_debug (this->name, 0, "Lease request from client: %s, " +                      "lease type:%d, lease cmd:%d, lease ID:%s, gfid:%s", +                      client_uid, lease->lease_type, lease->cmd, +                      leaseid_utoa (lease->lease_id), uuid_utoa (inode->gfid)); + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                switch (lease->cmd) { +                case GF_GET_LEASE: +                        lease->lease_type = lease_ctx->lease_type; +                        gf_msg_debug (this->name, 0, "Get lease, existing lease" +                                      "type: %d", lease_ctx->lease_type); +                        /*TODO:Should it consider lease id or client_uid?*/ +                        break; + +                case GF_SET_LEASE: +                        if (__is_lease_grantable (this, lease_ctx, lease, inode)) { +                                __add_lease (frame, inode, lease_ctx, +                                             client_uid, lease); +                                ret = 0; +                        } else { +                                gf_msg_debug (this->name, GF_LOG_DEBUG, +                                              "Not granting the conflicting lease" +                                              " request from %s on gfid(%s)", +                                              client_uid, uuid_utoa (inode->gfid)); +                                __recall_lease (this, lease_ctx); +                                ret = -1; +                        } +                        break; +                case GF_UNLK_LEASE: +                        ret = __remove_lease (this, inode, lease_ctx, +                                              client_uid, lease); +                        if ((ret == 0) && (lease_ctx->lease_cnt == 0)) { +                                pthread_mutex_unlock (&lease_ctx->lock); +                                goto unblock; +                        } +                        break; +                default: +                        ret = -EINVAL; +                        break; +                } +        } +        pthread_mutex_unlock (&lease_ctx->lock); + +        return ret; + +unblock: +        do_blocked_fops (this, lease_ctx); +out: +        return ret; +} + + +/* ret = 1 conflict + * ret = 0 no conflict + */ +gf_boolean_t +__check_lease_conflict (call_frame_t *frame, lease_inode_ctx_t *lease_ctx, +                        const char *lease_id, gf_boolean_t is_write) +{ +        gf_lease_types_t   lease_type      = {0,}; +        gf_boolean_t       conflicts       = _gf_false; +        lease_id_entry_t  *lease_entry     = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", frame, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out); +        GF_VALIDATE_OR_GOTO ("leases", lease_id, out); + +        lease_type = lease_ctx->lease_type; + +        /* If the fop is rename or unlink conflict the lease even if its +         * from the same client?? +         */ +        if ((frame->op == GF_FOP_RENAME) || (frame->op == GF_FOP_UNLINK)) { +                conflicts = _gf_true; +                goto recall; +        } + +        /* TODO: If lease_id is not sent, fall back to client uid conflict check? +         * Or set conflicts = true if lease_id is 0 when there is an existing +         * lease */ +        switch (lease_type) { +        case (GF_RW_LEASE | GF_RD_LEASE): +        case GF_RW_LEASE: +                lease_entry = __get_lease_id_entry (lease_ctx, lease_id); +                if (lease_entry && (lease_entry->lease_type & GF_RW_LEASE)) +                        conflicts = _gf_false; +                else +                        conflicts = _gf_true; +                break; +        case GF_RD_LEASE: +                if (is_write && __another_lease_found(lease_ctx, lease_id)) +                        conflicts = _gf_true; +                else +                        conflicts = _gf_false; +                break; +        default: +                break; +        } + +recall: +        /* If there is a conflict found and recall is not already sent to all +         * the clients, then send recall to each of the client holding lease. +         */ +        if (conflicts) +                __recall_lease (frame->this, lease_ctx); +out: +        return conflicts; +} + + +/* Return values: + * -1 : error, unwind the fop + * WIND_FOP: No conflict, wind the fop + * BLOCK_FOP: Found a conflicting lease, block the fop + */ +int +check_lease_conflict (call_frame_t *frame, inode_t *inode, +                      const char *lease_id, uint32_t fop_flags) +{ +        lease_inode_ctx_t       *lease_ctx  = NULL; +        gf_boolean_t       is_blocking_fop  = _gf_false; +        gf_boolean_t       is_write_fop     = _gf_false; +        gf_boolean_t       conflicts        = _gf_false; +        int                ret              = -1; + +        lease_ctx = lease_ctx_get (inode, frame->this); +        if (!lease_ctx) { +                gf_msg (frame->this->name, GF_LOG_WARNING, ENOMEM, +                        LEASE_MSG_NO_MEM, +                        "Unable to create/get inode ctx"); +                ret = -1; +                errno = ENOMEM; +                goto out; +        } + +        is_blocking_fop = fop_flags & BLOCKING_FOP; +        is_write_fop = fop_flags & DATA_MODIFY_FOP; + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                if (lease_ctx->lease_type == NONE) { +                        gf_msg_debug (frame->this->name, 0, +                                        "No leases found continuing with the" +                                        " fop:%s", gf_fop_list[frame->op]); +                        ret = WIND_FOP; +                        goto unlock; +                } +                conflicts = __check_lease_conflict (frame, lease_ctx, +                                lease_id, is_write_fop); +                if (conflicts) { +                        if (is_blocking_fop) { +                                gf_msg_debug (frame->this->name, 0, "Fop: %s " +                                              "conflicting existing " +                                              "lease: %d, blocking the" +                                              "fop", gf_fop_list[frame->op], +                                              lease_ctx->lease_type); +                                ret = BLOCK_FOP; +                        } else { +                                gf_msg_debug (frame->this->name, 0, "Fop: %s " +                                              "conflicting existing " +                                              "lease: %d, sending " +                                              "EAGAIN", gf_fop_list[frame->op], +                                              lease_ctx->lease_type); +                                errno = EAGAIN; +                                ret = -1; +                        } +                } +        } +unlock: +        pthread_mutex_unlock (&lease_ctx->lock); +out: +        return ret; +} + + +static int +remove_clnt_leases (const char *client_uid, inode_t *inode, xlator_t *this) +{ +        lease_inode_ctx_t  *lease_ctx      = NULL; +        lease_id_entry_t   *lease_entry    = NULL; +        lease_id_entry_t   *tmp            = NULL; +        int                 ret            = 0; +        int                 i              = 0; + +        lease_ctx = lease_ctx_get (inode, this); +        if (!lease_ctx) { +                gf_msg (this->name, GF_LOG_WARNING, ENOMEM, +                        LEASE_MSG_INVAL_INODE_CTX, +                        "Unable to create/get inode ctx"); +                ret = -1; +                errno = ENOMEM; +                goto out; +        } + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                list_for_each_entry_safe (lease_entry, tmp, +                                          &lease_ctx->lease_id_list, +                                          lease_id_list) { +                        if (strcmp (client_uid, lease_entry->client_uid) == 0) { +                                for (i = 0; i < GF_LEASE_MAX_TYPE; i++) { +                                        lease_ctx->lease_type_cnt[i] -= lease_entry->lease_type_cnt[i]; +                                } +                                lease_ctx->lease_cnt -= lease_entry->lease_cnt; +                                __destroy_lease_id_entry (lease_entry); +                                if (lease_ctx->lease_cnt == 0) { +                                        pthread_mutex_unlock (&lease_ctx->lock); +                                        goto unblock; +                                } +                        } +                } +        } +        pthread_mutex_unlock (&lease_ctx->lock); +out: +        return ret; + +unblock: +        do_blocked_fops (this, lease_ctx); +        return ret; +} + + +int +cleanup_client_leases (xlator_t *this, const char *client_uid) +{ +        lease_client_t    *clnt         = NULL; +        lease_client_t    *tmp          = NULL; +        struct list_head   cleanup_list = {0, }; +        lease_inode_t     *l_inode      = NULL; +        lease_inode_t     *tmp1         = NULL; +        leases_private_t  *priv         = NULL; +        int                ret          = 0; + +        priv = this->private; +        if (!priv) { +                ret = -1; +                errno = EINVAL; +                goto out; +        } + +        INIT_LIST_HEAD (&cleanup_list); +        pthread_mutex_lock (&priv->mutex); +        { +                list_for_each_entry_safe (clnt, tmp, &priv->client_list, client_list) { +                        if ((strcmp (clnt->client_uid, client_uid) == 0)) { +                                list_for_each_entry_safe (l_inode, tmp1, +                                                          &clnt->inode_list, list) { +                                        list_del_init (&l_inode->list); +                                        list_add_tail (&l_inode->list, &cleanup_list); +                                } +                                break; +                        } +                        __destroy_lease_client (clnt); +                } +        } +        pthread_mutex_unlock (&priv->mutex); + +        l_inode = tmp1 = NULL; +        list_for_each_entry_safe (l_inode, tmp1, &cleanup_list, list) { +                remove_clnt_leases (client_uid, l_inode->inode, this); +        } +out: +        return ret; +} + + +static void +__remove_all_leases (xlator_t *this, lease_inode_ctx_t *lease_ctx) +{ +        int                 i              = 0; +        lease_id_entry_t   *lease_entry    = NULL; +        lease_id_entry_t   *tmp            = NULL; + +        __dump_leases_info (this, lease_ctx); + +        list_for_each_entry_safe (lease_entry, tmp, +                                  &lease_ctx->lease_id_list, +                                  lease_id_list) { +                lease_entry->lease_cnt = 0; +                remove_from_clnt_list (this, lease_entry->client_uid, lease_ctx->inode); +                __destroy_lease_id_entry (lease_entry); +        } +        INIT_LIST_HEAD (&lease_ctx->lease_id_list); +        for (i = 0; i <= GF_LEASE_MAX_TYPE; i++) +                lease_ctx->lease_type_cnt[i] = 0; +        lease_ctx->lease_type = 0; +        lease_ctx->lease_cnt = 0; +        lease_ctx->recall_in_progress = _gf_false; +        inode_unref (lease_ctx->inode); +        lease_ctx->timer = NULL; + +        /* TODO: +         * - Mark the corresponding fd bad. Could be done on client side +         * as a result of recall +         * - Free the lease_ctx +         */ +        return; +} + + +static int +remove_all_leases (xlator_t *this, inode_t *inode) +{ +        lease_inode_ctx_t  *lease_ctx       = NULL; +        int                 ret             = 0; + +        GF_VALIDATE_OR_GOTO ("leases", inode, out); + +        lease_ctx = lease_ctx_get (inode, this); +        if (!lease_ctx) { +                gf_msg (this->name, GF_LOG_WARNING, ENOMEM, +                        LEASE_MSG_INVAL_INODE_CTX, +                        "Unable to create/get inode ctx"); +                ret = -1; +                errno = ENOMEM; +                goto out; +        } + +        pthread_mutex_lock (&lease_ctx->lock); +        { +                __remove_all_leases (this, lease_ctx); +        } +        pthread_mutex_unlock (&lease_ctx->lock); + +        do_blocked_fops (this, lease_ctx); +out: +        return ret; +} + + +void * +expired_recall_cleanup (void *data) +{ +        struct timespec      sleep_till      = {0, }; +        struct list_head     recall_cleanup_list; +        lease_inode_t       *recall_entry    = NULL; +        lease_inode_t       *tmp             = NULL; +        leases_private_t    *priv            = NULL; +        xlator_t            *this            = NULL; + +        GF_VALIDATE_OR_GOTO ("leases", data, out); + +        this = data; +        priv = this->private; + +        gf_msg_debug (this->name, 0, "Started the expired_recall_cleanup thread"); + +        while (1) { +                pthread_mutex_lock (&priv->mutex); +                { +                        if (priv->fini) { +                                pthread_mutex_unlock (&priv->mutex); +                                goto out; +                        } +                        INIT_LIST_HEAD (&recall_cleanup_list); +                        if (list_empty (&priv->recall_list)) { +                                sleep_till.tv_sec = time (NULL) + 600; +                                pthread_cond_timedwait (&priv->cond, &priv->mutex, +                                                        &sleep_till); +                        } +                        if (!list_empty (&priv->recall_list)) { +                                gf_msg_debug (this->name, 0, "Found expired recalls"); +                                list_for_each_entry_safe (recall_entry, tmp, +                                                          &priv->recall_list, list) { +                                        list_del_init (&recall_entry->list); +                                        list_add_tail (&recall_entry->list, &recall_cleanup_list); +                                } +                        } +                } +                pthread_mutex_unlock (&priv->mutex); + +                recall_entry = tmp = NULL; +                list_for_each_entry_safe (recall_entry, tmp, &recall_cleanup_list, list) { +                        gf_msg_debug (this->name, 0, "Recall lease was sent on" +                                      " inode:%p, recall timer has expired" +                                      " and clients haven't unlocked the lease" +                                      " hence cleaning up leases on the inode", +                                      recall_entry->inode); +                        remove_all_leases (this, recall_entry->inode); +                        list_del_init (&recall_entry->list); +                } +        } + +out: +        return NULL; +}  | 
