[asterisk-commits] murf: trunk r114190 - in /trunk: ./ channels/ configs/ doc/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Apr 16 18:53:28 CDT 2008


Author: murf
Date: Wed Apr 16 18:53:27 2008
New Revision: 114190

URL: http://svn.digium.com/view/asterisk?view=rev&rev=114190
Log:
This is the scariest commit I've done in a long time. This is the astobj2-ification of chan_sip. I've tested a number of scenarios like crazy. It used to have 4x the call setup/teardown performance of trunk, but now it's roughly at parity. I will attempt to find the bottlenecks and get it back to the 4x mark. The changes made were somewhat invasive, but the value to the community of these upgrades outweighs waiting further for more testing. Every change being made to chan_sip was lousing this code up when we tried to merge. Peers, Users, Dialogs, are all now astobj2 objects, indexed via hashtables. Refcounting is used to track objects and free them at the bitter end of their lives. Please file issues on bugs.digium.com, and PLEASE, please, please be patient. One natural advantage to all the hash-table work is that loading large sip.conf files full of thousands of peers now goes much faster. One more please: PLEASE help thrash this code and test it.

Added:
    trunk/doc/chan_sip-perf-testing.txt   (with props)
Modified:
    trunk/CHANGES
    trunk/channels/chan_sip.c
    trunk/configs/sip.conf.sample

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=114190&r1=114189&r2=114190
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Wed Apr 16 18:53:27 2008
@@ -35,6 +35,13 @@
  * The ATTENDED_TRANSFER_COMPLETE_SOUND can now be set using setvar to cause a given
    audio file to be played upon completion of an attended transfer.
  * Added DNS manager support to registrations for peers referencing peer entries.
+ * Performance improvements via using hash tables (astobj2) and doubly-linked lists to improve 
+   load/reload of large numbers of peers/users by ~40x (for large lists of peers.
+   Initially, we saw 4x improvement in call setup/destruction, but at the time
+   of merging, this gain has disappeared; further research will be done to try
+   and restore this performance improvement. Astobj2 refcounting is now used
+   for users, peers, and dialogs.  Users are encouraged to assist in regression
+   testing and problem reporting!
 
 IAX Changes
 -----------

Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?view=diff&rev=114190&r1=114189&r2=114190
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Wed Apr 16 18:53:27 2008
@@ -1,4 +1,4 @@
-/*
+ /*
  * Asterisk -- An open source telephony toolkit.
  *
  * Copyright (C) 1999 - 2006, Digium, Inc.
@@ -136,6 +136,7 @@
 #include <signal.h>
 #include <sys/signal.h>
 #include <regex.h>
+#include <time.h>
 
 #include "asterisk/network.h"
 #include "asterisk/paths.h"	/* need ast_config_AST_SYSTEM_NAME */
@@ -163,6 +164,19 @@
 #include "asterisk/utils.h"
 #include "asterisk/file.h"
 #include "asterisk/astobj.h"
+/* 
+   Uncomment the define below,  if you are having refcount related memory leaks.
+   With this uncommented, this module will generate a file, /tmp/refs, which contains
+   a history of the ao2_ref() calls. To be useful, all calls to ao2_* functions should
+   be modified to ao2_t_* calls, and include a tag describing what is happening with 
+   enough detail, to make pairing up a reference count increment with its corresponding decrement.
+   The refcounter program in utils/ can be invaluable in highlighting objects that are not
+   balanced, along with the complete history for that object.
+   In normal operation, the macros defined will throw away the tags, so they do not 
+   affect the speed of the program at all. They can be considered to be documentation.
+*/
+/* #define  REF_DEBUG 1 */
+#include "asterisk/astobj2.h"
 #include "asterisk/dnsmgr.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/linkedlists.h"
@@ -1161,7 +1175,6 @@
  */
 struct sip_pvt {
 	struct sip_pvt *next;			/*!< Next dialog in chain */
-	ast_mutex_t pvt_lock;			/*!< Dialog private lock */
 	enum invitestates invitestate;		/*!< Track state of SIP_INVITEs */
 	int method;				/*!< SIP method that opened this dialog */
 	AST_DECLARE_STRING_FIELDS(
@@ -1314,43 +1327,48 @@
  * the container and individual items, and functions to add/remove
  * references to the individual items.
  */
-static struct sip_pvt *dialoglist = NULL;
-
-/*! \brief Protect the SIP dialog list (of sip_pvt's) */
-AST_MUTEX_DEFINE_STATIC(dialoglock);
-
-#ifndef DETECT_DEADLOCKS
-/*! \brief hide the way the list is locked/unlocked */
-static void dialoglist_lock(void)
-{
-	ast_mutex_lock(&dialoglock);
-}
-
-static void dialoglist_unlock(void)
-{
-	ast_mutex_unlock(&dialoglock);
-}
-#else
-/* we don't want to HIDE the information about where the lock was requested if trying to debug 
- * deadlocks!  So, just make these macros! */
-#define dialoglist_lock(x) ast_mutex_lock(&dialoglock)
-#define dialoglist_unlock(x) ast_mutex_unlock(&dialoglock)
-#endif
+struct ao2_container *dialogs;
 
 /*!
  * when we create or delete references, make sure to use these
  * functions so we keep track of the refcounts.
  * To simplify the code, we allow a NULL to be passed to dialog_unref().
  */
-static struct sip_pvt *dialog_ref(struct sip_pvt *p)
-{
+#ifdef REF_DEBUG
+#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+static struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+{
+	if (p)
+		_ao2_ref_debug(p, 1, tag, file, line, func);
+	else
+		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
 	return p;
 }
 
-static struct sip_pvt *dialog_unref(struct sip_pvt *p)
-{
+static struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+{
+	if (p)
+		_ao2_ref_debug(p, -1, tag, file, line, func);
 	return NULL;
 }
+#else
+static struct sip_pvt *dialog_ref(struct sip_pvt *p, char *tag)
+{
+	if (p)
+		ao2_ref(p, 1);
+	else
+		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
+	return p;
+}
+
+static struct sip_pvt *dialog_unref(struct sip_pvt *p, char *tag)
+{
+	if (p)
+		ao2_ref(p, -1);
+	return NULL;
+}
+#endif
 
 /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
  * Packets are linked in a list, whose head is in the struct sip_pvt they belong to.
@@ -1376,7 +1394,7 @@
 /*! \brief Structure for SIP user data. User's place calls to us */
 struct sip_user {
 	/* Users who can access various contexts */
-	ASTOBJ_COMPONENTS(struct sip_user);
+	char name[80];
 	char secret[80];		/*!< Password */
 	char md5secret[80];		/*!< Password in md5 */
 	char context[AST_MAX_CONTEXT];	/*!< Default context for incoming calls */
@@ -1396,6 +1414,7 @@
 
 	/* things that don't belong in flags */
 	char is_realtime;		/*!< this is a 'realtime' user */
+	unsigned int the_mark:1;          /*!< moved out of the ASTOBJ fields; that which bears the_mark should be deleted! */
 
 	int amaflags;			/*!< AMA flags for billing */
 	int callingpres;		/*!< Calling id presentation */
@@ -1427,8 +1446,7 @@
 /*! \brief Structure for SIP peer data, we place calls to peers if registered  or fixed IP address (host) */
 /* XXX field 'name' must be first otherwise sip_addrcmp() will fail */
 struct sip_peer {
-	ASTOBJ_COMPONENTS(struct sip_peer);	/*!< name, refcount, objflags,  object pointers */
-					/*!< peer->name is the unique name of this object */
+	char name[80];			/*!< peer->name is the unique name of this object */
 	struct sip_socket socket;	/*!< Socket used for this peer */
 	char secret[80];		/*!< Password */
 	char md5secret[80];		/*!< Password in MD5 */
@@ -1470,6 +1488,7 @@
 	char rt_fromcontact;		/*!< P: copy fromcontact from realtime */
 	char host_dynamic;		/*!< P: Dynamic Peers register with Asterisk */
 	char selfdestruct;		/*!< P: Automatic peers need to destruct themselves */
+	char the_mark;           /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */
 
 	int expire;			/*!<  When to expire this peer registration */
 	int capability;			/*!<  Codec capability */
@@ -1560,26 +1579,129 @@
 	AST_LIST_ENTRY(sip_threadinfo) list;
 };
 
-/* --- Linked lists of various objects --------*/
+/* --- Hash tables of various objects --------*/
+
+#ifdef LOW_MEMORY
+static int hash_peer_size = 17;
+static int hash_dialog_size = 17;
+static int hash_user_size = 17;
+#else
+static int hash_peer_size = 563;
+static int hash_dialog_size = 563;
+static int hash_user_size = 563;
+#endif
 
 /*! \brief  The thread list of TCP threads */
 static AST_LIST_HEAD_STATIC(threadl, sip_threadinfo);
 
 /*! \brief  The user list: Users and friends */
-static struct ast_user_list {
-	ASTOBJ_CONTAINER_COMPONENTS(struct sip_user);
-} userl;
+static struct ao2_container *users;
 
 /*! \brief  The peer list: Peers and Friends */
-static struct ast_peer_list {
-	ASTOBJ_CONTAINER_COMPONENTS(struct sip_peer);
-} peerl;
+struct ao2_container *peers;
+struct ao2_container *peers_by_ip;
 
 /*! \brief  The register list: Other SIP proxies we register with and place calls to */
 static struct ast_register_list {
 	ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
 	int recheck;
 } regl;
+
+/*!
+ * \note The only member of the peer used here is the name field
+ */
+static int peer_hash_cb(const void *obj, const int flags)
+{
+	const struct sip_peer *peer = obj;
+
+	return ast_str_hash(peer->name);
+}
+
+/*!
+ * \note The only member of the peer used here is the name field
+ */
+static int peer_cmp_cb(void *obj, void *arg, int flags)
+{
+	struct sip_peer *peer = obj, *peer2 = arg;
+
+	return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
+ */
+static int peer_iphash_cb(const void *obj, const int flags)
+{
+	const struct sip_peer *peer = obj;
+	int ret1 = peer->addr.sin_addr.s_addr;
+	if (ret1 < 0)
+		ret1 = -ret1;
+	
+	if (ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT)) {
+		return ret1;
+	} else {
+		return ret1 + peer->addr.sin_port;
+	}
+}
+
+/*!
+ * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
+ */
+static int peer_ipcmp_cb(void *obj, void *arg, int flags)
+{
+	struct sip_peer *peer = obj, *peer2 = arg;
+
+	if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr)
+		return 0;
+	
+	if (!ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) && !ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) {
+		if (peer->addr.sin_port == peer2->addr.sin_port)
+			return CMP_MATCH;
+		else
+			return 0;
+	}
+	return CMP_MATCH;
+}
+
+/*!
+ * \note The only member of the user used here is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+	const struct sip_user *user = obj;
+
+	return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user used here is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+	struct sip_user *user = obj, *user2 = arg;
+
+	return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note The only member of the dialog used here callid string
+ */
+static int dialog_hash_cb(const void *obj, const int flags)
+{
+	const struct sip_pvt *pvt = obj;
+
+	return ast_str_hash(pvt->callid);
+}
+
+/*!
+ * \note The only member of the dialog used here callid string
+ */
+static int dialog_cmp_cb(void *obj, void *arg, int flags)
+{
+	struct sip_pvt *pvt = obj, *pvt2 = arg;
+	
+	return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH : 0;
+}
 
 static int temp_pvt_init(void *);
 static void temp_pvt_cleanup(void *);
@@ -1741,7 +1863,9 @@
 static void sip_scheddestroy(struct sip_pvt *p, int ms);
 static int sip_cancel_destroy(struct sip_pvt *p);
 static struct sip_pvt *sip_destroy(struct sip_pvt *p);
-static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
+static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist);
+static void *registry_unref(struct sip_registry *reg, char *tag);
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
 static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
 static void __sip_pretend_ack(struct sip_pvt *p);
 static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
@@ -1799,12 +1923,14 @@
 static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm);
 
 /*--- Misc functions */
+static void check_rtp_timeout(struct sip_pvt *dialog, time_t t);
 static int sip_do_reload(enum channelreloadreason reason);
 static int reload_config(enum channelreloadreason reason);
 static int expire_register(const void *data);
 static void *do_monitor(void *data);
 static int restart_monitor(void);
-static int sip_addrcmp(char *name, struct sockaddr_in *sin);	/* Support for peer matching */
+static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
+/* static int sip_addrcmp(char *name, struct sockaddr_in *sin);	Support for peer matching */
 static int sip_refer_allocate(struct sip_pvt *p);
 static void ast_quiet_chan(struct ast_channel *chan);
 static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
@@ -1825,8 +1951,11 @@
 static const char *nat2str(int nat) attribute_const;
 static int peer_status(struct sip_peer *peer, char *status, int statuslen);
 static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
 static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *_sip_dbdump(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
+static char *sip_dbdump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static void  print_group(int fd, ast_group_t group, int crlf);
 static const char *dtmfmode2str(int mode) attribute_const;
@@ -1887,7 +2016,9 @@
 static struct sip_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime);
 static int update_call_counter(struct sip_pvt *fup, int event);
 static void sip_destroy_peer(struct sip_peer *peer);
+static void sip_destroy_peer_fn(void *peer);
 static void sip_destroy_user(struct sip_user *user);
+static void sip_destroy_user_fn(void *user);
 static int sip_poke_peer(struct sip_peer *peer);
 static void set_peer_defaults(struct sip_peer *peer);
 static struct sip_peer *temp_peer(const char *name);
@@ -1919,6 +2050,7 @@
 static int __sip_do_register(struct sip_registry *r);
 static int sip_reg_timeout(const void *data);
 static void sip_send_all_registers(void);
+static int sip_reinvite_retry(const void *data);
 
 /*--- Parsing SIP requests and responses */
 static void append_date(struct sip_request *req);	/* Append date to SIP packet */
@@ -2267,26 +2399,93 @@
 	return NULL;
 }
 
-#define sip_pvt_lock(x) ast_mutex_lock(&x->pvt_lock)
-#define sip_pvt_trylock(x) ast_mutex_trylock(&x->pvt_lock)
-#define sip_pvt_unlock(x) ast_mutex_unlock(&x->pvt_lock)
+#define sip_pvt_lock(x) ao2_lock(x)
+#define sip_pvt_trylock(x) ao2_trylock(x)
+#define sip_pvt_unlock(x) ao2_unlock(x)
 
 /*!
  * helper functions to unreference various types of objects.
  * By handling them this way, we don't have to declare the
  * destructor on each call, which removes the chance of errors.
  */
-static void unref_peer(struct sip_peer *peer)
-{
-	ASTOBJ_UNREF(peer, sip_destroy_peer);
-}
-
-static void unref_user(struct sip_user *user)
-{
-	ASTOBJ_UNREF(user, sip_destroy_user);
-}
-
-static void *registry_unref(struct sip_registry *reg)
+static void *unref_peer(struct sip_peer *peer, char *tag)
+{
+	ao2_t_ref(peer, -1, tag);
+	return NULL;
+}
+
+static void *unref_user(struct sip_user *user, char *tag)
+{
+	ao2_t_ref(user, -1, tag);
+	return NULL;
+}
+
+static struct sip_peer *ref_peer(struct sip_peer *peer, char *tag)
+{
+	ao2_t_ref(peer, 1,tag);
+	return peer;
+}
+
+/*!
+ * \brief Unlink a dialog from the dialogs container, as well as any other places
+ * that it may be currently stored.
+ *
+ * \note A reference to the dialog must be held before calling this function, and this
+ * function does not release that reference.
+ */
+static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist)
+{
+	struct sip_pkt *cp;
+
+	dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
+
+	ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
+
+	/* Unlink us from the owner (channel) if we have one */
+	if (dialog->owner) {
+		if (lockowner)
+			ast_channel_lock(dialog->owner);
+		ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
+		dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
+		if (lockowner)
+			ast_channel_unlock(dialog->owner);
+	}
+	if (dialog->registry) {
+		if (dialog->registry->call == dialog)
+			dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
+		dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
+	}
+	if (dialog->stateid > -1) {
+		ast_extension_state_del(dialog->stateid, NULL);
+		dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
+		dialog->stateid = -1; /* shouldn't we 'zero' this out? */
+	}
+	/* Remove link from peer to subscription of MWI */
+	if (dialog->relatedpeer && dialog->relatedpeer->mwipvt)
+		dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+	if (dialog->relatedpeer && dialog->relatedpeer->call == dialog)
+		dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+
+	/* remove all current packets in this dialog */
+	while((cp = dialog->packets)) {
+		dialog->packets = dialog->packets->next;
+		AST_SCHED_DEL(sched, cp->retransid);
+		dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
+		ast_free(cp);
+	}
+
+	AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
+
+	AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
+	
+	if (dialog->autokillid > -1)
+		AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
+
+	dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
+	return NULL;
+}
+
+static void *registry_unref(struct sip_registry *reg, char *tag)
 {
 	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
 	ASTOBJ_UNREF(reg, sip_registry_destroy);
@@ -2294,7 +2493,7 @@
 }
 
 /*! \brief Add object reference to SIP registry */
-static struct sip_registry *registry_addref(struct sip_registry *reg)
+static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
 {
 	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
 	return ASTOBJ_REF(reg);	/* Add pointer to registry in packet */
@@ -2689,7 +2888,7 @@
 	struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
 	int reschedule = DEFAULT_RETRANS;
 	int xmitres = 0;
-
+	
 	/* Lock channel PVT */
 	sip_pvt_lock(pkt->owner);
 
@@ -2792,8 +2991,11 @@
 		if (cur == pkt) {
 			UNLINK(cur, pkt->owner->packets, prev);
 			sip_pvt_unlock(pkt->owner);
+			if (pkt->owner)
+				pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
 			if (pkt->data)
 				ast_free(pkt->data);
+			pkt->data = NULL;
 			ast_free(pkt);
 			return 0;
 		}
@@ -2822,7 +3024,7 @@
 	/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
 	/* According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
 	if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
-		xmitres = __sip_xmit(dialog_ref(p), data, len);	/* Send packet */
+		xmitres = __sip_xmit(dialog_ref(p, "pasing dialog ptr into callback..."), data, len);	/* Send packet */
 		if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
 			append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
 			return AST_FAILURE;
@@ -2844,7 +3046,7 @@
 	pkt->seqno = seqno;
 	pkt->is_resp = resp;
 	pkt->is_fatal = fatal;
-	pkt->owner = dialog_ref(p);
+	pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
 	pkt->next = p->packets;
 	p->packets = pkt;	/* Add it to the queue */
 	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
@@ -2861,6 +3063,7 @@
 
 	if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
 		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+		ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
 		if (pkt->data)
 			ast_free(pkt->data);
 		return AST_FAILURE;
@@ -2896,7 +3099,7 @@
 
 	if (p->subscribed == MWI_NOTIFICATION)
 		if (p->relatedpeer)
-			unref_peer(p->relatedpeer);	/* Remove link to peer. If it's realtime, make sure it's gone from memory) */
+			p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer");	/* Remove link to peer. If it's realtime, make sure it's gone from memory) */
 
 	/* Reset schedule ID */
 	p->autokillid = -1;
@@ -2904,19 +3107,20 @@
 	if (p->owner) {
 		ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
 		ast_queue_hangup(p->owner);
-		dialog_unref(p);
 	} else if (p->refer) {
 		ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
 		transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
 		append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-		dialog_unref(p);
 	} else {
 		append_history(p, "AutoDestroy", "%s", p->callid);
 		ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
-		sip_destroy(p);		/* Go ahead and destroy dialog. All attempts to recover is done */
+		dialog_unlink_all(p, TRUE, TRUE); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
+		/* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
+		/* sip_destroy(p); */		/* Go ahead and destroy dialog. All attempts to recover is done */
 		/* sip_destroy also absorbs the reference */
 	}
+	dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
 	return 0;
 }
 
@@ -2937,7 +3141,7 @@
 
 	if (p->do_history)
 		append_history(p, "SchedDestroy", "%d ms", ms);
-	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p));
+	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
 
 	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0)
 		stop_session_timer(p);
@@ -2951,11 +3155,13 @@
 {
 	int res = 0;
 	if (p->autokillid > -1) {
-		if (!(res = ast_sched_del(sched, p->autokillid))) {
+		int res3;
+		
+		if (!(res3 = ast_sched_del(sched, p->autokillid))) {
 			append_history(p, "CancelDestroy", "");
 			p->autokillid = -1;
-		}
-		dialog_unref(p);
+			dialog_unref(p, "dialog unrefd because autokillid is de-sched'd");
+		}
 	}
 	return res;
 }
@@ -3010,7 +3216,7 @@
 				sip_pvt_lock(p);
 			}
 			UNLINK(cur, p->packets, prev);
-			dialog_unref(cur->owner);
+			dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
 			if (cur->data)
 				ast_free(cur->data);
 			ast_free(cur);
@@ -3491,27 +3697,36 @@
 		destroy_mailbox(mailbox);
 }
 
+static void sip_destroy_peer_fn(void *peer)
+{
+	sip_destroy_peer(peer);
+}
+
 /*! \brief Destroy peer object from memory */
 static void sip_destroy_peer(struct sip_peer *peer)
 {
 	ast_debug(3, "Destroying SIP peer %s\n", peer->name);
-
 	if (peer->outboundproxy)
 		ast_free(peer->outboundproxy);
 	peer->outboundproxy = NULL;
 
 	/* Delete it, it needs to disappear */
-	if (peer->call)
-		peer->call = sip_destroy(peer->call);
-
-	if (peer->mwipvt) 	/* We have an active subscription, delete it */
-		peer->mwipvt = sip_destroy(peer->mwipvt);
-
+	if (peer->call) {
+		dialog_unlink_all(peer->call, TRUE, TRUE);
+		peer->call = dialog_unref(peer->call, "peer->call is being unset");
+	}
+	
+
+	if (peer->mwipvt) {	/* We have an active subscription, delete it */
+		dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
+		peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
+	}
+	
 	if (peer->chanvars) {
 		ast_variables_destroy(peer->chanvars);
 		peer->chanvars = NULL;
 	}
-
+	
 	/* If the schedule delete fails, that means the schedule is currently
 	 * running, which means we should wait for that thread to complete.
 	 * Otherwise, there's a crashable race condition.
@@ -3524,18 +3739,17 @@
 	register_peer_exten(peer, FALSE);
 	ast_free_ha(peer->ha);
 	if (peer->selfdestruct)
-		apeerobjs--;
+		ast_atomic_fetchadd_int(&apeerobjs, -1);
 	else if (peer->is_realtime) {
-		rpeerobjs--;
+		ast_atomic_fetchadd_int(&rpeerobjs, -1);
 		ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
 	} else
-		speerobjs--;
+		ast_atomic_fetchadd_int(&speerobjs, -1);
 	clear_realm_authentication(peer->auth);
 	peer->auth = NULL;
 	if (peer->dnsmgr)
 		ast_dnsmgr_release(peer->dnsmgr);
 	clear_peer_mailboxes(peer);
-	ast_free(peer);
 }
 
 /*! \brief Update peer data in database (if used) */
@@ -3578,6 +3792,8 @@
 /*! \brief  realtime_peer: Get peer from realtime storage
  * Checks the "sippeers" realtime family from extconfig.conf 
  * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
+ * This returns a pointer to a peer and because we use build_peer, we can rest
+ * assured that the refcount is bumped.
 */
 static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin)
 {
@@ -3743,8 +3959,14 @@
 		ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
 		if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
 			AST_SCHED_REPLACE(peer->expire, sched, global_rtautoclear * 1000, expire_register, (void *) peer);
-		}
-		ASTOBJ_CONTAINER_LINK(&peerl, peer);
+			/* we could be incr. its refcount right here, but I guess, since
+			   peers hang around until module unload time anyway, it's not worth the trouble */
+		}
+		ao2_t_link(peers, peer, "link peer into peers table");
+		if (peer->addr.sin_addr.s_addr) {
+			ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
+		}
+		
 	} else {
 		peer->is_realtime = 1;
 	}
@@ -3758,36 +3980,45 @@
 	return peer;
 }
 
-/*! \brief Support routine for find_peer */
-static int sip_addrcmp(char *name, struct sockaddr_in *sin)
-{
-	/* We know name is the first field, so we can cast */
-	struct sip_peer *p = (struct sip_peer *) name;
-	return 	!(!inaddrcmp(&p->addr, sin) || 
-					(ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) &&
-					(p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
-}
-
 /*! \brief Locate peer by name or ip address 
  *	This is used on incoming SIP message to find matching peer on ip
 	or outgoing message to find matching peer on name 
-	\note Avoid using this function in new functions if there's a way to avoid it, i
-	since it causes a database lookup or a traversal of the in-memory peer list.
+	\note Avoid using this function in new functions if there is a way to avoid it, i
+	since it might cause a database lookup.
 */
 static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
 {
 	struct sip_peer *p = NULL;
-
-	if (peer)
-		p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
-	else
-		p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
+	struct sip_peer tmp_peer;
+	
+	if (peer) {
+		ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
+		p = ao2_t_find(peers, &tmp_peer, OBJ_POINTER, "ao2_find in peers table");
+	} else if (sin) { /* search by addr? */
+		tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+		tmp_peer.addr.sin_port = sin->sin_port;
+		tmp_peer.flags[0].flags = 0;
+		p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+		if (!p) {
+			ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
+			p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+			if (p) {
+				return p;
+			}
+		}
+	}
 
 	if (!p && realtime)
 		p = realtime_peer(peer, sin);
 
 	return p;
 }
+
+static void sip_destroy_user_fn(void *user)
+{
+	sip_destroy_user(user);
+}
+
 
 /*! \brief Remove user object from in-memory storage */
 static void sip_destroy_user(struct sip_user *user)
@@ -3800,14 +4031,14 @@
 		user->chanvars = NULL;
 	}
 	if (user->is_realtime)
-		ruserobjs--;
+		ast_atomic_fetchadd_int(&ruserobjs, -1);
 	else
-		suserobjs--;
-	ast_free(user);
+		ast_atomic_fetchadd_int(&suserobjs, -1);
 }
 
 /*! \brief Load user from realtime storage
  * Loads user from "sipusers" category in realtime (extconfig.conf)
+ * returns a refcounted pointer to a sip_user structure.
  * Users are matched on From: user name (the domain in skipped) */
 static struct sip_user *realtime_user(const char *username)
 {
@@ -3837,12 +4068,12 @@
 
 	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
 		ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
-		suserobjs++;
-		ASTOBJ_CONTAINER_LINK(&userl, user);
+		ast_atomic_fetchadd_int(&suserobjs, 1);
+		ao2_t_link(users, user, "link user into users table");
 	} else {
 		/* Move counter from s to r... */
-		suserobjs--;
-		ruserobjs++;
+		ast_atomic_fetchadd_int(&suserobjs, -1);
+		ast_atomic_fetchadd_int(&ruserobjs, 1);
 		user->is_realtime = 1;
 	}
 	ast_variables_destroy(var);
@@ -3852,10 +4083,20 @@
 /*! \brief Locate user by name 
  * Locates user by name (From: sip uri user name part) first
  * from in-memory list (static configuration) then from 
- * realtime storage (defined in extconfig.conf) */
+ * realtime storage (defined in extconfig.conf) 
+ * \return a refcounted pointer to a user. Make sure the
+ * the ref count is decremented when this pointer is nulled, changed,
+ * or goes out of scope!
+ */
+
 static struct sip_user *find_user(const char *name, int realtime)
 {
-	struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name);
+	struct sip_user tmp;
+	struct sip_user *u;
+
+	ast_copy_string(tmp.name, name, sizeof(tmp.name));
+	u = ao2_t_find(users, &tmp, OBJ_POINTER, "ao2_find in users table");
+
 	if (!u && realtime)
 		u = realtime_user(name);
 	return u;
@@ -3935,6 +4176,7 @@
  *	again from memory or database during the life time of the dialog.
  *
  * \return -1 on error, 0 on success.
+ *
  */
 static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
 {
@@ -4018,11 +4260,13 @@
 		if (!dialog->initreq.headers) {
 			char *c;
 			char *tmpcall = ast_strdupa(dialog->callid);
-
+			/* this sure looks to me like we are going to change the callid on this dialog!! */
 			c = strchr(tmpcall, '@');
 			if (c) {
 				*c = '\0';
+				ao2_t_unlink(dialogs, dialog, "About to change the callid -- remove the old name");
 				ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
+				ao2_t_link(dialogs, dialog, "New dialog callid -- inserted back into table");
 			}
 		}
 	}
@@ -4082,7 +4326,7 @@
 
 	if (peer) {
 		int res = create_addr_from_peer(dialog, peer);
-		unref_peer(peer);
+		unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup");
 		return res;
 	} else {
 		/* Setup default parameters for this dialog's socket. Currently we only support regular UDP SIP as the default */
@@ -4150,14 +4394,13 @@
 	if (p->owner) {
 		/* XXX fails on possible deadlock */
 		if (!ast_channel_trylock(p->owner)) {
-			ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
 			append_history(p, "Cong", "Auto-congesting (timer)");
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
 			ast_channel_unlock(p->owner);
 		}
 	}
 	sip_pvt_unlock(p);
-	dialog_unref(p);
+	dialog_unref(p, "unreffing arg passed into auto_congest callback (p->initid)");
 	return 0;
 }
 
@@ -4245,9 +4488,11 @@
 		p->invitestate = INV_CALLING;
 	
 		/* Initialize auto-congest time */
-		AST_SCHED_REPLACE(p->initid, sched, p->timer_b, auto_congest, dialog_ref(p));
-	}
-
+		AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p, 
+								dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"), 
+								dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
+								dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
+	}
 	return res;
 }
 
@@ -4261,35 +4506,25 @@
 	if (reg->call) {
 		/* Clear registry before destroying to ensure
 		   we don't get reentered trying to grab the registry lock */
-		reg->call->registry = NULL;
+		reg->call->registry = registry_unref(reg->call->registry, "destroy reg->call->registry");
 		ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
-		reg->call = sip_destroy(reg->call);
-	}
-	AST_SCHED_DEL(sched, reg->expire);
+		dialog_unlink_all(reg->call, TRUE, TRUE);
+		reg->call = dialog_unref(reg->call, "unref reg->call");
+		/* reg->call = sip_destroy(reg->call); */
+	}
+	AST_SCHED_DEL(sched, reg->expire);	
 	AST_SCHED_DEL(sched, reg->timeout);
+	
 	ast_string_field_free_memory(reg);
-	regobjs--;
+	ast_atomic_fetchadd_int(&regobjs, -1);
 	ast_dnsmgr_release(reg->dnsmgr);
 	ast_free(reg);
 	
 }
 
 /*! \brief Execute destruction of SIP dialog structure, release memory */
-static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
-{
-	struct sip_pvt *cur, *prev = NULL;
-	struct sip_pkt *cp;
-
-	/* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */
-	if (p->rtp && ast_rtp_get_bridged(p->rtp)) {
-		ast_verbose("Bridge still active.  Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
-		return -1;
-	}
-
-	if (p->vrtp && ast_rtp_get_bridged(p->vrtp)) {
-		ast_verbose("Bridge still active.  Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
-		return -1;
-	}
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
+{
 
 	if (sip_debug_test_pvt(p))
 		ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
@@ -4315,20 +4550,25 @@
 	}
 
 	/* Remove link from peer to subscription of MWI */
-	if (p->relatedpeer && p->relatedpeer->mwipvt) 
-		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt);
-
+	if (p->relatedpeer && p->relatedpeer->mwipvt)
+		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+	if (p->relatedpeer && p->relatedpeer->call == p)
+		p->relatedpeer->call = dialog_unref(p->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+	
+	if (p->relatedpeer)
+		p->relatedpeer = unref_peer(p->relatedpeer,"unsetting a dialog relatedpeer field in sip_destroy");
+	
+	if (p->registry) {
+		if (p->registry->call == p)
+			p->registry->call = dialog_unref(p->registry->call, "nulling out the registry's call dialog field in unlink_all");
+		p->registry = registry_unref(p->registry, "delete p->registry");
+	}
+	
 	if (dumphistory)
 		sip_dump_history(p);
 
 	if (p->options)
 		ast_free(p->options);
-
-	if (p->stateid > -1)
-		ast_extension_state_del(p->stateid, NULL);
-	AST_SCHED_DEL(sched, p->initid);
-	AST_SCHED_DEL(sched, p->waitid);
-	AST_SCHED_DEL(sched, p->autokillid);
 
 	if (p->rtp) {
 		ast_rtp_destroy(p->rtp);
@@ -4349,11 +4589,6 @@
 		free_old_route(p->route);
 		p->route = NULL;
 	}
-	if (p->registry) {
-		if (p->registry->call == p)
-			p->registry->call = NULL;
-		p->registry = registry_unref(p->registry);
-	}
 	if (p->initreq.data)
 		ast_free(p->initreq.data);
 
@@ -4362,6 +4597,7 @@
 		if (p->stimer->st_active == TRUE && p->stimer->st_schedid > -1)
 			ast_sched_del(sched, p->stimer->st_schedid);
 		ast_free(p->stimer);
+		p->stimer = NULL;
 	}
 
 	/* Clear history */
@@ -4375,41 +4611,13 @@
 		p->history = NULL;
 	}
 
-	/* Lock dialog list before removing ourselves from the list */
-	if (lockdialoglist)
-		dialoglist_lock();
-	for (prev = NULL, cur = dialoglist; cur; prev = cur, cur = cur->next) {
-		if (cur == p) {
-			UNLINK(cur, dialoglist, prev);
-			break;
-		}
-	}
-	if (lockdialoglist)
-		dialoglist_unlock();
-	if (!cur) {
-		ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
-		return 0;
-	} 
-
-	/* remove all current packets in this dialog */
-	while((cp = p->packets)) {
-		p->packets = p->packets->next;
-		AST_SCHED_DEL(sched, cp->retransid);
-		dialog_unref(cp->owner);
-		if (cp->data)
-			ast_free(cp->data);
-		ast_free(cp);
-	}
 	if (p->chanvars) {
 		ast_variables_destroy(p->chanvars);
 		p->chanvars = NULL;
 	}
-	ast_mutex_destroy(&p->pvt_lock);
 
 	ast_string_field_free_memory(p);
-
-	ast_free(p);
-	return 0;
+	return;
 }
 
 /*! \brief  update_call_counter: Handle call_limit for SIP users 
@@ -4490,9 +4698,9 @@
 			if (*inuse >= *call_limit) {
 				ast_log(LOG_WARNING, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
 				if (u)
-					unref_user(u);
+					unref_user(u, "update_call_counter: unref user u call limit exceeded");
 				else
-					unref_peer(p);
+					unref_peer(p, "update_call_counter: unref peer p, call limit exceeded");
 				return -1; 
 			}
 		}
@@ -4522,10 +4730,16 @@
 	}
 	if (p) {
 		ast_device_state_changed("SIP/%s", p->name);
-		unref_peer(p);
+		unref_peer(p, "update_call_counter: unref_peer from call counter");
 	} else /* u must be set */
-		unref_user(u);
+		unref_user(u, "update_call_counter: unref_user from call counter");
 	return 0;
+}
+
+
+static void sip_destroy_fn(void *p)
+{
+	sip_destroy(p);
 }
 
 /*! \brief Destroy SIP call structure.
@@ -4731,8 +4945,10 @@
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Really hang up next time */
 		p->needdestroy = 0;
-		p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt);
+		p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt");
+		sip_pvt_lock(p);
 		p->owner = NULL;  /* Owner will be gone after we return, so take it away */
+		sip_pvt_unlock(p);
 		return 0;
 	}
 
@@ -4773,7 +4989,7 @@

[... 3343 lines stripped ...]



More information about the asterisk-commits mailing list