[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(&regexbuf, 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(&regexbuf);
@@ -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(&registrations);
 	while ((reg = AST_LIST_REMOVE_HEAD(&registrations, 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