[asterisk-commits] russell: branch russell/iax_refcount r79908 - /team/russell/iax_refcount/chan...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Aug 17 14:28:02 CDT 2007
Author: russell
Date: Fri Aug 17 14:28:01 2007
New Revision: 79908
URL: http://svn.digium.com/view/asterisk?view=rev&rev=79908
Log:
It occurred to me that the handling of IAX2 users suffered from the same
reference handling problems as peers, so I have converted them to use astobj2
as well. Now we ensure that a user won't get free'd while any references to
it still exist. As an extremely nice side benefit, the related code got
a lot more efficient, just like peer handling. There is no need to hold the
container locked for long periods like before. Also, the objects are in a
hash table instead of a single linked list.
Modified:
team/russell/iax_refcount/channels/chan_iax2.c
Modified: team/russell/iax_refcount/channels/chan_iax2.c
URL: http://svn.digium.com/view/asterisk/team/russell/iax_refcount/channels/chan_iax2.c?view=diff&rev=79908&r1=79907&r2=79908
==============================================================================
--- team/russell/iax_refcount/channels/chan_iax2.c (original)
+++ team/russell/iax_refcount/channels/chan_iax2.c Fri Aug 17 14:28:01 2007
@@ -632,14 +632,15 @@
int count;
} iaxq;
-static AST_LIST_HEAD_STATIC(users, iax2_user);
-
#ifdef LOW_MEMORY
#define MAX_PEER_BUCKETS 17
#else
#define MAX_PEER_BUCKETS 563
#endif
static ao2_container *peers;
+
+#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
+static ao2_container *users;
static struct ast_firmware_list {
struct iax_firmware *wares;
@@ -832,7 +833,6 @@
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
-static void destroy_user(struct iax2_user *user);
static void prune_peers(void);
static const struct ast_channel_tech iax2_tech = {
@@ -1067,6 +1067,26 @@
}
/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_user *user = obj;
+
+ return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj, *user2 = arg;
+
+ return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
* \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
* so do not call it with a pvt lock held.
*/
@@ -1092,9 +1112,21 @@
return peer;
}
-static struct iax2_peer *peer_unref(struct iax2_peer *peer)
+static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
{
ao2_ref(peer, -1);
+ return NULL;
+}
+
+static inline struct iax2_user *user_ref(struct iax2_user *user)
+{
+ ao2_ref(user, +1);
+ return user;
+}
+
+static inline struct iax2_user *user_unref(struct iax2_user *user)
+{
+ ao2_ref(user, -1);
return NULL;
}
@@ -1837,18 +1869,19 @@
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
- struct iax2_user *user = NULL;
-
/* Decrement AUTHREQ count if needed */
if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, pvt->username)) {
- user->curauthreq--;
- break;
- }
- }
- AST_LIST_UNLOCK(&users);
+ struct iax2_user *user;
+ struct iax2_user tmp_user = {
+ .name = pvt->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
+ user_unref(user);
+ }
+
ast_clear_flag(pvt, IAX_MAXAUTHREQ);
}
/* No more pings or lagrq's */
@@ -2740,9 +2773,7 @@
if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
ast_set_flag(user, IAX_RTCACHEFRIENDS);
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user_ref(user));
} else {
ast_set_flag(user, IAX_TEMPONLY);
}
@@ -4169,6 +4200,7 @@
struct iax2_user *user = NULL;
char auth[90];
char *pstr = "";
+ ao2_iterator i;
switch (argc) {
case 5:
@@ -4185,8 +4217,9 @@
}
ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref");
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ for (user = ao2_iterator_next(&i); user;
+ user_unref(user), user = ao2_iterator_next(&i)) {
if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0))
continue;
@@ -4207,9 +4240,7 @@
ast_cli(fd, FORMAT2, user->name, auth, user->authmethods,
user->contexts ? user->contexts->context : context,
user->ha ? "Yes" : "No", pstr);
-
- }
- AST_LIST_UNLOCK(&users);
+ }
if (havepattern)
regfree(®exbuf);
@@ -4792,6 +4823,7 @@
int bestscore = 0;
int gotcapability = 0;
struct ast_variable *v = NULL, *tmpvar = NULL;
+ ao2_iterator i;
if (!iaxs[callno])
return res;
@@ -4846,8 +4878,8 @@
return res;
}
/* Search the userlist for a compatible entry, and fill in the rest */
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
!strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
&& ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */
@@ -4855,6 +4887,8 @@
apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
if (!ast_strlen_zero(iaxs[callno]->username)) {
/* Exact match, stop right now. */
+ if (best)
+ user_unref(best);
best = user;
break;
} else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->inkeys)) {
@@ -4863,13 +4897,19 @@
/* There was host authentication and we passed, bonus! */
if (bestscore < 4) {
bestscore = 4;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* No host access, but no secret, either, not bad */
if (bestscore < 3) {
bestscore = 3;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
} else {
@@ -4877,26 +4917,31 @@
/* Authentication, but host access too, eh, it's something.. */
if (bestscore < 2) {
bestscore = 2;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* Authentication and no host access... This is our baseline */
if (bestscore < 1) {
bestscore = 1;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
}
}
- }
- AST_LIST_UNLOCK(&users);
+ user_unref(user);
+ }
user = best;
if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
user = realtime_user(iaxs[callno]->username);
if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */
!apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */
- destroy_user(user);
- user = NULL;
+ user = user_unref(user);
}
}
if (user) {
@@ -4975,9 +5020,8 @@
ast_string_field_set(iaxs[callno], secret, buf);
} else
ast_string_field_set(iaxs[callno], secret, user->secret);
- if (ast_test_flag(user, IAX_TEMPONLY))
- destroy_user(user);
res = 0;
+ user = user_unref(user);
}
ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);
return res;
@@ -5023,7 +5067,6 @@
*/
static int authenticate_request(int call_num)
{
- struct iax2_user *user = NULL;
struct iax_ie_data ied;
int res = -1, authreq_restrict = 0;
char challenge[10];
@@ -5033,17 +5076,18 @@
/* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username)) {
- if (user->curauthreq == user->maxauthreq)
- authreq_restrict = 1;
- else
- user->curauthreq++;
- break;
- }
- }
- AST_LIST_UNLOCK(&users);
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ if (user->curauthreq == user->maxauthreq)
+ authreq_restrict = 1;
+ else
+ user->curauthreq++;
+ user = user_unref(user);
+ }
}
/* If the AUTHREQ limit test failed, send back an error */
@@ -5082,21 +5126,19 @@
char rsasecret[256] = "";
int res = -1;
int x;
- struct iax2_user *user = NULL;
-
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username))
- break;
- }
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
if (user) {
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- user->curauthreq--;
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
ast_clear_flag(p, IAX_MAXAUTHREQ);
}
ast_string_field_set(p, host, user->name);
- }
- AST_LIST_UNLOCK(&users);
+ user = user_unref(user);
+ }
if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
return res;
@@ -9083,6 +9125,20 @@
return peer;
}
+static void user_destructor(void *obj)
+{
+ struct iax2_user *user = obj;
+
+ ast_free_ha(user->ha);
+ free_context(user->contexts);
+ if(user->vars) {
+ ast_variables_destroy(user->vars);
+ user->vars = NULL;
+ }
+ ast_string_field_free_pools(user);
+ free(user);
+}
+
/*! \brief Create in-memory user structure from configuration */
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
@@ -9095,18 +9151,15 @@
int oldcurauthreq = 0;
char *varname = NULL, *varval = NULL;
struct ast_variable *tmpvar = NULL;
-
- AST_LIST_LOCK(&users);
+ struct iax2_user tmp_user = {
+ .name = name,
+ };
+
if (!temponly) {
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, name)) {
- if (!ast_test_flag(user, IAX_DELME))
- firstpass = 0;
- break;
- }
- }
- } else
- user = NULL;
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user && !ast_test_flag(user, IAX_DELME))
+ firstpass = 0;
+ }
if (user) {
if (firstpass) {
@@ -9117,12 +9170,9 @@
user->contexts = NULL;
}
/* Already in the list, remove it and it will be added back (or FREE'd) */
- AST_LIST_REMOVE(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_unlink(users, user);
} else {
- AST_LIST_UNLOCK(&users);
- /* This is going to memset'd to 0 in the next block */
- user = ast_calloc(sizeof(*user),1);
+ user = ao2_alloc(sizeof(*user), user_destructor);
}
if (user) {
@@ -9130,8 +9180,7 @@
ast_string_field_free_pools(user);
memset(user, 0, sizeof(struct iax2_user));
if (ast_string_field_init(user, 32)) {
- free(user);
- user = NULL;
+ user = user_unref(user);
}
user->maxauthreq = maxauthreq;
user->curauthreq = oldcurauthreq;
@@ -9311,15 +9360,20 @@
return 0;
}
+static int user_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj;
+
+ ast_set_flag(user, IAX_DELME);
+
+ return 0;
+}
+
static void delete_users(void)
{
- struct iax2_user *user;
struct iax2_registry *reg;
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry)
- ast_set_flag(user, IAX_DELME);
- AST_LIST_UNLOCK(&users);
+ ao2_callback(users, 0, user_delme_cb, NULL);
AST_LIST_LOCK(®istrations);
while ((reg = AST_LIST_REMOVE_HEAD(®istrations, entry))) {
@@ -9342,32 +9396,17 @@
ao2_callback(peers, 0, peer_delme_cb, NULL);
}
-static void destroy_user(struct iax2_user *user)
-{
- ast_free_ha(user->ha);
- free_context(user->contexts);
- if(user->vars) {
- ast_variables_destroy(user->vars);
- user->vars = NULL;
- }
- ast_string_field_free_pools(user);
- free(user);
-}
-
static void prune_users(void)
{
- struct iax2_user *user = NULL;
-
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, entry) {
- if (ast_test_flag(user, IAX_DELME)) {
- destroy_user(user);
- AST_LIST_REMOVE_CURRENT(&users, entry);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&users);
-
+ struct iax2_user *user;
+ ao2_iterator i;
+
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
+ if (ast_test_flag(user, IAX_DELME))
+ ao2_unlink(users, user);
+ user_unref(user);
+ }
}
/* Prune peers who still are supposed to be deleted */
@@ -9685,15 +9724,15 @@
/* Start with general parameters, then specific parameters, user and peer */
user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (user) {
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user);
+ user = NULL;
}
peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (peer) {
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
ao2_link(peers, peer);
+ peer = NULL;
}
}
if (ast_true(registeriax) || (!registeriax && genregisteriax)) {
@@ -9729,9 +9768,8 @@
if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
if (user) {
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user);
+ user = NULL;
}
}
if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
@@ -9740,6 +9778,7 @@
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
ao2_link(peers, peer);
+ peer = NULL;
}
} else if (strcasecmp(utype, "user")) {
ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file);
@@ -10568,6 +10607,7 @@
ast_mutex_destroy(&iaxsl[x]);
ao2_ref(peers, -1);
+ ao2_ref(users, -1);
return 0;
}
@@ -10599,6 +10639,11 @@
peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
if (!peers)
return AST_MODULE_LOAD_FAILURE;
+ users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
+ if (!users) {
+ ao2_ref(peers, -1);
+ return AST_MODULE_LOAD_FAILURE;
+ }
ast_custom_function_register(&iaxpeer_function);
More information about the asterisk-commits
mailing list