diff options
author | Shehjar Tikoo <shehjart@gluster.com> | 2009-06-15 13:05:39 +0000 |
---|---|---|
committer | Anand V. Avati <avati@dev.gluster.com> | 2009-06-15 23:44:36 -0700 |
commit | efcce990960fb91d422630fc7b310b216a500fed (patch) | |
tree | 7c417d43464c26476d9dfd541c13c2bf05f9b2bf /libglusterfs | |
parent | 7006ae207c9e8ab9685d8e2e7455bd4e3b217c97 (diff) |
libglusterfs: Turn fd-table O(1)
This commit reduces CPU usage of gf_fd_unused_get drastically by
making it O(1) instead of O(n).
Related to: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=16
Signed-off-by: Anand V. Avati <avati@dev.gluster.com>
Diffstat (limited to 'libglusterfs')
-rw-r--r-- | libglusterfs/src/fd.c | 171 | ||||
-rw-r--r-- | libglusterfs/src/fd.h | 20 |
2 files changed, 125 insertions, 66 deletions
diff --git a/libglusterfs/src/fd.c b/libglusterfs/src/fd.c index fc78acc7c..cfebf1489 100644 --- a/libglusterfs/src/fd.c +++ b/libglusterfs/src/fd.c @@ -77,10 +77,32 @@ gf_roundup_power_of_two (uint32_t nr) return result; } +static int +gf_fd_chain_fd_entries (fdentry_t *entries, uint32_t startidx, + uint32_t endcount) +{ + uint32_t i = 0; + + if (!entries) + return -1; + + /* Chain only till the second to last entry because we want to + * ensure that the last entry has GF_FDTABLE_END. + */ + for (i = startidx; i < (endcount - 1); i++) + entries[i].next_free = i + 1; + + /* i has already been incremented upto the last entry. */ + entries[i].next_free = GF_FDTABLE_END; + + return 0; +} + + static uint32_t gf_fd_fdtable_expand (fdtable_t *fdtable, uint32_t nr) { - fd_t **oldfds = NULL; + fdentry_t *oldfds = NULL; uint32_t oldmax_fds = -1; if (fdtable == NULL || nr < 0) @@ -89,22 +111,30 @@ gf_fd_fdtable_expand (fdtable_t *fdtable, uint32_t nr) return EINVAL; } - nr /= (1024 / sizeof (fd_t *)); + nr /= (1024 / sizeof (fdentry_t)); nr = gf_roundup_power_of_two (nr + 1); - nr *= (1024 / sizeof (fd_t *)); + nr *= (1024 / sizeof (fdentry_t)); - oldfds = fdtable->fds; + oldfds = fdtable->fdentries; oldmax_fds = fdtable->max_fds; - fdtable->fds = CALLOC (nr, sizeof (fd_t *)); - ERR_ABORT (fdtable->fds); + fdtable->fdentries = CALLOC (nr, sizeof (fdentry_t)); + ERR_ABORT (fdtable->fdentries); fdtable->max_fds = nr; if (oldfds) { - uint32_t cpy = oldmax_fds * sizeof (fd_t *); - memcpy (fdtable->fds, oldfds, cpy); + uint32_t cpy = oldmax_fds * sizeof (fdentry_t); + memcpy (fdtable->fdentries, oldfds, cpy); } + gf_fd_chain_fd_entries (fdtable->fdentries, oldmax_fds, + fdtable->max_fds); + + /* Now that expansion is done, we must update the fd list + * head pointer so that the fd allocation functions can continue + * using the expanded table. + */ + fdtable->first_free = oldmax_fds; FREE (oldfds); return 0; } @@ -129,36 +159,36 @@ gf_fd_fdtable_alloc (void) return fdtable; } -fd_t ** +fdentry_t * __gf_fd_fdtable_get_all_fds (fdtable_t *fdtable, uint32_t *count) { - fd_t **fds = NULL; + fdentry_t *fdentries = NULL; if (count == NULL) { goto out; } - fds = fdtable->fds; - fdtable->fds = calloc (fdtable->max_fds, sizeof (fd_t *)); + fdentries = fdtable->fdentries; + fdtable->fdentries = calloc (fdtable->max_fds, sizeof (fdentry_t)); *count = fdtable->max_fds; out: - return fds; + return fdentries; } -fd_t ** +fdentry_t * gf_fd_fdtable_get_all_fds (fdtable_t *fdtable, uint32_t *count) { - fd_t **fds = NULL; + fdentry_t *entries = NULL; if (fdtable) { pthread_mutex_lock (&fdtable->lock); { - fds = __gf_fd_fdtable_get_all_fds (fdtable, count); + entries = __gf_fd_fdtable_get_all_fds (fdtable, count); } pthread_mutex_unlock (&fdtable->lock); } - return fds; + return entries; } void @@ -166,31 +196,31 @@ gf_fd_fdtable_destroy (fdtable_t *fdtable) { struct list_head list = {0, }; fd_t *fd = NULL; - fd_t **fds = NULL; + fdentry_t *fdentries = NULL; uint32_t fd_count = 0; int32_t i = 0; INIT_LIST_HEAD (&list); - if (fdtable) { - pthread_mutex_lock (&fdtable->lock); - { - fds = __gf_fd_fdtable_get_all_fds (fdtable, &fd_count); - FREE (fdtable->fds); - } - pthread_mutex_unlock (&fdtable->lock); + if (!fdtable) + return; - if (fds != NULL) { - for (i = 0; i < fd_count; i++) { - fd = fds[i]; - if (fd != NULL) { - fd_unref (fd); - } - } + pthread_mutex_lock (&fdtable->lock); + { + fdentries = __gf_fd_fdtable_get_all_fds (fdtable, &fd_count); + FREE (fdtable->fdentries); + } + pthread_mutex_unlock (&fdtable->lock); - FREE (fds); + if (fdentries != NULL) { + for (i = 0; i < fd_count; i++) { + fd = fdentries[i].fd; + if (fd != NULL) { + fd_unref (fd); + } } + FREE (fdentries); pthread_mutex_destroy (&fdtable->lock); FREE (fdtable); } @@ -222,19 +252,10 @@ gf_fd_unused_get2 (fdtable_t *fdtable, fd_t *fdptr, int32_t fd) } } - if (!fdtable->fds[fd]) - { - fdtable->fds[fd] = fdptr; - fd_ref (fdptr); - ret = fd; - } - else - { - gf_log ("fd.c", - GF_LOG_ERROR, - "Cannot allocate fd %d (slot not empty in fdtable)", fd); - } - } + fdtable->fdentries[fd].fd = fdptr; + fd_ref (fdptr); + ret = fd; + } err: pthread_mutex_unlock (&fdtable->lock); @@ -245,7 +266,10 @@ err: int32_t gf_fd_unused_get (fdtable_t *fdtable, fd_t *fdptr) { - int32_t fd = -1, i = 0; + int32_t fd = -1; + fdentry_t *fde = NULL; + int error; + int alloc_attempts = 0; if (fdtable == NULL || fdptr == NULL) { @@ -255,28 +279,42 @@ gf_fd_unused_get (fdtable_t *fdtable, fd_t *fdptr) pthread_mutex_lock (&fdtable->lock); { - for (i = 0; i<fdtable->max_fds; i++) - { - if (!fdtable->fds[i]) - break; - } - - if (i < fdtable->max_fds) { - fdtable->fds[i] = fdptr; - fd = i; +fd_alloc_try_again: + if (fdtable->first_free != GF_FDTABLE_END) { + fde = &fdtable->fdentries[fdtable->first_free]; + fd = fdtable->first_free; + fdtable->first_free = fde->next_free; + fde->next_free = GF_FDENTRY_ALLOCATED; + fde->fd = fdptr; } else { - int32_t error; - error = gf_fd_fdtable_expand (fdtable, fdtable->max_fds + 1); + /* If this is true, there is something + * seriously wrong with our data structures. + */ + if (alloc_attempts >= 2) { + gf_log ("server-protocol.c", GF_LOG_ERROR, + "Multiple attempts to expand fd table" + " have failed."); + goto out; + } + error = gf_fd_fdtable_expand (fdtable, + fdtable->max_fds + 1); if (error) { gf_log ("server-protocol.c", GF_LOG_ERROR, "Cannot expand fdtable:%s", strerror (error)); - } else { - fdtable->fds[i] = fdptr; - fd = i; + goto out; } + ++alloc_attempts; + /* At this point, the table stands expanded + * with the first_free referring to the first + * free entry in the new set of fdentries that + * have just been allocated. That means, the + * above logic should just work. + */ + goto fd_alloc_try_again; } } +out: pthread_mutex_unlock (&fdtable->lock); return fd; @@ -287,6 +325,8 @@ inline void gf_fd_put (fdtable_t *fdtable, int32_t fd) { fd_t *fdptr = NULL; + fdentry_t *fde = NULL; + if (fdtable == NULL || fd < 0) { gf_log ("fd", GF_LOG_ERROR, "invalid argument"); @@ -301,8 +341,11 @@ gf_fd_put (fdtable_t *fdtable, int32_t fd) pthread_mutex_lock (&fdtable->lock); { - fdptr = fdtable->fds[fd]; - fdtable->fds[fd] = NULL; + fde = &fdtable->fdentries[fd]; + fdptr = fde->fd; + fde->fd = NULL; + fde->next_free = fdtable->first_free; + fdtable->first_free = fd; } pthread_mutex_unlock (&fdtable->lock); @@ -333,7 +376,7 @@ gf_fd_fdptr_get (fdtable_t *fdtable, int64_t fd) pthread_mutex_lock (&fdtable->lock); { - fdptr = fdtable->fds[fd]; + fdptr = fdtable->fdentries[fd].fd; if (fdptr) { fd_ref (fdptr); } diff --git a/libglusterfs/src/fd.h b/libglusterfs/src/fd.h index d3c9afde3..b4bb12118 100644 --- a/libglusterfs/src/fd.h +++ b/libglusterfs/src/fd.h @@ -51,14 +51,30 @@ struct _fd { }; typedef struct _fd fd_t; +struct fd_table_entry { + fd_t *fd; + int next_free; +}; +typedef struct fd_table_entry fdentry_t; + struct _fdtable { int refcount; uint32_t max_fds; pthread_mutex_t lock; - fd_t **fds; + fdentry_t *fdentries; + int first_free; }; typedef struct _fdtable fdtable_t; +/* Signifies no more entries in the fd table. */ +#define GF_FDTABLE_END -1 + +/* The value us the same as GF_FDTABLE_END but the + * purpose is different here. This is used to invalidated + * the next_free value in an fdentry that has been allocated + */ +#define GF_FDENTRY_ALLOCATED -1 + #include "logging.h" #include "xlator.h" @@ -77,7 +93,7 @@ gf_fd_unused_get (fdtable_t *fdtable, fd_t *fdptr); int32_t gf_fd_unused_get2 (fdtable_t *fdtable, fd_t *fdptr, int32_t fd); -fd_t ** +fdentry_t * gf_fd_fdtable_get_all_fds (fdtable_t *fdtable, uint32_t *count); void |