astobj2-ify the SLA code
[asterisk/asterisk.git] / apps / app_meetme.c
index b79ab63..7071960 100644 (file)
@@ -961,17 +961,26 @@ struct sla_station {
        /*! This option uses the values in the sla_hold_access enum and sets the
         * access control type for hold on this station. */
        unsigned int hold_access:1;
-       /*! Use count for inside sla_station_exec */
-       unsigned int ref_count;
+       /*! Mark used during reload processing */
+       unsigned int mark:1;
 };
 
+/*!
+ * \brief A reference to a station
+ *
+ * This struct looks near useless at first glance.  However, its existence
+ * in the list of stations in sla_trunk means that this station references
+ * that trunk.  We use the mark to keep track of whether it needs to be
+ * removed from the sla_trunk's list of stations during a reload.
+ */
 struct sla_station_ref {
        AST_LIST_ENTRY(sla_station_ref) entry;
        struct sla_station *station;
+       /*! Mark used during reload processing */
+       unsigned int mark:1;
 };
 
 struct sla_trunk {
-       AST_RWLIST_ENTRY(sla_trunk) entry;
        AST_DECLARE_STRING_FIELDS(
                AST_STRING_FIELD(name);
                AST_STRING_FIELD(device);
@@ -995,10 +1004,16 @@ struct sla_trunk {
        /*! Whether this trunk is currently on hold, meaning that once a station
         *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
        unsigned int on_hold:1;
-       /*! Use count for inside sla_trunk_exec */
-       unsigned int ref_count;
+       /*! Mark used during reload processing */
+       unsigned int mark:1;
 };
 
+/*!
+ * \brief A station's reference to a trunk
+ *
+ * An sla_station keeps a list of trunk_refs.  This holds metadata about the
+ * stations usage of the trunk.
+ */
 struct sla_trunk_ref {
        AST_LIST_ENTRY(sla_trunk_ref) entry;
        struct sla_trunk *trunk;
@@ -1012,10 +1027,12 @@ struct sla_trunk_ref {
         *  station.  This takes higher priority than a ring delay set at
         *  the station level. */
        unsigned int ring_delay;
+       /*! Mark used during reload processing */
+       unsigned int mark:1;
 };
 
-static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
-static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
+static struct ao2_container *sla_stations;
+static struct ao2_container *sla_trunks;
 
 static const char sla_registrar[] = "SLA";
 
@@ -1027,10 +1044,6 @@ enum sla_event_type {
        SLA_EVENT_DIAL_STATE,
        /*! The state of a ringing trunk has changed */
        SLA_EVENT_RINGING_TRUNK,
-       /*! A reload of configuration has been requested */
-       SLA_EVENT_RELOAD,
-       /*! Poke the SLA thread so it can check if it can perform a reload */
-       SLA_EVENT_CHECK_RELOAD,
 };
 
 struct sla_event {
@@ -1086,8 +1099,6 @@ static struct {
        /*! Attempt to handle CallerID, even though it is known not to work
         *  properly in some situations. */
        unsigned int attempt_callerid:1;
-       /*! A reload has been requested */
-       unsigned int reload:1;
 } sla = {
        .thread = AST_PTHREADT_NULL,
 };
@@ -2117,7 +2128,8 @@ static const char *sla_hold_str(unsigned int hold_access)
 
 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       const struct sla_trunk *trunk;
+       struct ao2_iterator i;
+       struct sla_trunk *trunk;
 
        switch (cmd) {
        case CLI_INIT:
@@ -2135,12 +2147,17 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
                    "=== Configured SLA Trunks ===================================\n"
                    "=============================================================\n"
                    "===\n");
-       AST_RWLIST_RDLOCK(&sla_trunks);
-       AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+       i = ao2_iterator_init(sla_trunks, 0);
+       for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
                struct sla_station_ref *station_ref;
                char ring_timeout[16] = "(none)";
-               if (trunk->ring_timeout)
+
+               ao2_lock(trunk);
+
+               if (trunk->ring_timeout) {
                        snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
+               }
+
                ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                            "=== Trunk Name:       %s\n"
                            "=== ==> Device:       %s\n"
@@ -2154,13 +2171,16 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
                            ring_timeout,
                            trunk->barge_disabled ? "No" : "Yes",
                            sla_hold_str(trunk->hold_access));
-               AST_RWLIST_RDLOCK(&sla_stations);
-               AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
+
+               AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
                        ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
-               AST_RWLIST_UNLOCK(&sla_stations);
+               }
+
                ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
+
+               ao2_unlock(trunk);
        }
-       AST_RWLIST_UNLOCK(&sla_trunks);
+       ao2_iterator_destroy(&i);
        ast_cli(a->fd, "=============================================================\n\n");
 
        return CLI_SUCCESS;
@@ -2182,7 +2202,8 @@ static const char *trunkstate2str(enum sla_trunk_state state)
 
 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       const struct sla_station *station;
+       struct ao2_iterator i;
+       struct sla_station *station;
 
        switch (cmd) {
        case CLI_INIT:
@@ -2200,11 +2221,14 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                    "=== Configured SLA Stations =================================\n"
                    "=============================================================\n"
                    "===\n");
-       AST_RWLIST_RDLOCK(&sla_stations);
-       AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+       i = ao2_iterator_init(sla_stations, 0);
+       for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
                struct sla_trunk_ref *trunk_ref;
                char ring_timeout[16] = "(none)";
                char ring_delay[16] = "(none)";
+
+               ao2_lock(station);
+
                if (station->ring_timeout) {
                        snprintf(ring_timeout, sizeof(ring_timeout), 
                                "%u", station->ring_timeout);
@@ -2225,7 +2249,6 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                            S_OR(station->autocontext, "(none)"), 
                            ring_timeout, ring_delay,
                            sla_hold_str(station->hold_access));
-               AST_RWLIST_RDLOCK(&sla_trunks);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
                        if (trunk_ref->ring_timeout) {
                                snprintf(ring_timeout, sizeof(ring_timeout),
@@ -2245,11 +2268,12 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                    trunkstate2str(trunk_ref->state),
                                    ring_timeout, ring_delay);
                }
-               AST_RWLIST_UNLOCK(&sla_trunks);
                ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                            "===\n");
+
+               ao2_unlock(station);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
+       ao2_iterator_destroy(&i);
        ast_cli(a->fd, "============================================================\n"
                    "\n");
 
@@ -2393,11 +2417,16 @@ static void sla_queue_event_full(enum sla_event_type type,
        struct sla_event *event;
 
        if (sla.thread == AST_PTHREADT_NULL) {
+               ao2_ref(station, -1);
+               ao2_ref(trunk_ref, -1);
                return;
        }
 
-       if (!(event = ast_calloc(1, sizeof(*event))))
+       if (!(event = ast_calloc(1, sizeof(*event)))) {
+               ao2_ref(station, -1);
+               ao2_ref(trunk_ref, -1);
                return;
+       }
 
        event->type = type;
        event->trunk_ref = trunk_ref;
@@ -2431,6 +2460,7 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
        struct sla_station *station;
        struct sla_trunk_ref *trunk_ref = NULL;
        char *trunk_name;
+       struct ao2_iterator i;
 
        trunk_name = ast_strdupa(conf->confno);
        strsep(&trunk_name, "_");
@@ -2439,16 +2469,23 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
                return;
        }
 
-       AST_RWLIST_RDLOCK(&sla_stations);
-       AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+       i = ao2_iterator_init(sla_stations, 0);
+       while ((station = ao2_iterator_next(&i))) {
+               ao2_lock(station);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-                       if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
+                       if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
+                               ao2_ref(trunk_ref, 1);
                                break;
+                       }
                }
-               if (trunk_ref)
+               ao2_unlock(station);
+               if (trunk_ref) {
+                       /* station reference given to sla_queue_event_full() */
                        break;
+               }
+               ao2_ref(station, -1);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
+       ao2_iterator_destroy(&i);
 
        if (!trunk_ref) {
                ast_debug(1, "Trunk not found for event!\n");
@@ -5776,34 +5813,30 @@ static void load_config_meetme(void)
        ast_config_destroy(cfg);
 }
 
-/*! \brief Find an SLA trunk by name
- * \note This must be called with the sla_trunks container locked
+/*!
+ * \internal
+ * \brief Find an SLA trunk by name
  */
 static struct sla_trunk *sla_find_trunk(const char *name)
 {
-       struct sla_trunk *trunk = NULL;
-
-       AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-               if (!strcasecmp(trunk->name, name))
-                       break;
-       }
+       struct sla_trunk tmp_trunk = {
+               .name = name,
+       };
 
-       return trunk;
+       return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
 }
 
-/*! \brief Find an SLA station by name
- * \note This must be called with the sla_stations container locked
+/*!
+ * \internal
+ * \brief Find an SLA station by name
  */
 static struct sla_station *sla_find_station(const char *name)
 {
-       struct sla_station *station = NULL;
-
-       AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
-               if (!strcasecmp(station->name, name))
-                       break;
-       }
+       struct sla_station tmp_station = {
+               .name = name,
+       };
 
-       return station;
+       return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
 }
 
 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
@@ -5827,9 +5860,11 @@ static int sla_check_station_hold_access(const struct sla_trunk *trunk,
        return 0;
 }
 
-/*! \brief Find a trunk reference on a station by name
+/*!
+ * \brief Find a trunk reference on a station by name
  * \param station the station
  * \param name the trunk's name
+ * \pre sla_station is locked
  * \return a pointer to the station's trunk reference.  If the trunk
  *         is not found, it is not idle and barge is disabled, or if
  *         it is on hold and private hold is set, then NULL will be returned.
@@ -5856,16 +5891,32 @@ static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station
                break;
        }
 
+       if (trunk_ref) {
+               ao2_ref(trunk_ref, 1);
+       }
+
        return trunk_ref;
 }
 
+static void sla_station_ref_destructor(void *obj)
+{
+       struct sla_station_ref *station_ref = obj;
+
+       if (station_ref->station) {
+               ao2_ref(station_ref->station, -1);
+               station_ref->station = NULL;
+       }
+}
+
 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
 {
        struct sla_station_ref *station_ref;
 
-       if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
+       if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
                return NULL;
+       }
 
+       ao2_ref(station, 1);
        station_ref->station = station;
 
        return station_ref;
@@ -5878,12 +5929,48 @@ static struct sla_ringing_station *sla_create_ringing_station(struct sla_station
        if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
                return NULL;
 
+       ao2_ref(station, 1);
        ringing_station->station = station;
        ringing_station->ring_begin = ast_tvnow();
 
        return ringing_station;
 }
 
+static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
+{
+       if (ringing_station->station) {
+               ao2_ref(ringing_station->station, -1);
+               ringing_station->station = NULL;
+       }
+
+       ast_free(ringing_station);
+}
+
+static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
+{
+       struct sla_failed_station *failed_station;
+
+       if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
+               return NULL;
+       }
+
+       ao2_ref(station, 1);
+       failed_station->station = station;
+       failed_station->last_try = ast_tvnow();
+
+       return failed_station;
+}
+
+static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
+{
+       if (failed_station->station) {
+               ao2_ref(failed_station->station, -1);
+               failed_station->station = NULL;
+       }
+
+       ast_free(failed_station);
+}
+
 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
 {
        switch (state) {
@@ -5906,18 +5993,25 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk
 {
        struct sla_station *station;
        struct sla_trunk_ref *trunk_ref;
+       struct ao2_iterator i;
 
-       AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+       i = ao2_iterator_init(sla_stations, 0);
+       while ((station = ao2_iterator_next(&i))) {
+               ao2_lock(station);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
                        if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
-                               || trunk_ref == exclude)
+                                       || trunk_ref == exclude) {
                                continue;
+                       }
                        trunk_ref->state = state;
                        ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
                                             "SLA:%s_%s", station->name, trunk->name);
                        break;
                }
+               ao2_unlock(station);
+               ao2_ref(station, -1);
        }
+       ao2_iterator_destroy(&i);
 }
 
 struct run_station_args {
@@ -5935,8 +6029,8 @@ static void answer_trunk_chan(struct ast_channel *chan)
 
 static void *run_station(void *data)
 {
-       struct sla_station *station;
-       struct sla_trunk_ref *trunk_ref;
+       RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
        struct ast_str *conf_name = ast_str_create(16);
        struct ast_flags64 conf_flags = { 0 };
        struct ast_conference *conf;
@@ -5979,6 +6073,8 @@ static void *run_station(void *data)
        return NULL;
 }
 
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
+
 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
 {
        char buf[80];
@@ -5988,10 +6084,11 @@ static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
        admin_exec(NULL, buf);
        sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
 
-       while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
-               ast_free(station_ref);
+       while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
+               ao2_ref(station_ref, -1);
+       }
 
-       ast_free(ringing_trunk);
+       sla_ringing_trunk_destroy(ringing_trunk);
 }
 
 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
@@ -6026,7 +6123,7 @@ static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station
        }
 
 done:
-       ast_free(ringing_station);
+       sla_ringing_station_destroy(ringing_station);
 }
 
 static void sla_dial_state_callback(struct ast_dial *dial)
@@ -6078,8 +6175,10 @@ static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *st
                        if (rm)
                                AST_LIST_REMOVE_CURRENT(entry);
 
-                       if (trunk_ref)
+                       if (trunk_ref) {
+                               ao2_ref(s_trunk_ref, 1);
                                *trunk_ref = s_trunk_ref;
+                       }
 
                        break;
                }
@@ -6097,7 +6196,7 @@ static void sla_handle_dial_state_event(void)
        struct sla_ringing_station *ringing_station;
 
        AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
-               struct sla_trunk_ref *s_trunk_ref = NULL;
+               RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
                struct sla_ringing_trunk *ringing_trunk = NULL;
                struct run_station_args args;
                enum ast_dial_result dial_res;
@@ -6130,7 +6229,7 @@ static void sla_handle_dial_state_event(void)
                                ast_dial_join(ringing_station->station->dial);
                                ast_dial_destroy(ringing_station->station->dial);
                                ringing_station->station->dial = NULL;
-                               ast_free(ringing_station);
+                               sla_ringing_station_destroy(ringing_station);
                                break;
                        }
                        /* Track the channel that answered this trunk */
@@ -6141,12 +6240,14 @@ static void sla_handle_dial_state_event(void)
                        /* Now, start a thread that will connect this station to the trunk.  The rest of
                         * the code here sets up the thread and ensures that it is able to save the arguments
                         * before they are no longer valid since they are allocated on the stack. */
+                       ao2_ref(s_trunk_ref, 1);
                        args.trunk_ref = s_trunk_ref;
+                       ao2_ref(ringing_station->station, 1);
                        args.station = ringing_station->station;
                        args.cond = &cond;
                        args.cond_lock = &cond_lock;
-                       ast_free(ringing_trunk);
-                       ast_free(ringing_station);
+                       sla_ringing_trunk_destroy(ringing_trunk);
+                       sla_ringing_station_destroy(ringing_station);
                        ast_mutex_init(&cond_lock);
                        ast_cond_init(&cond, NULL);
                        ast_mutex_lock(&cond_lock);
@@ -6200,7 +6301,7 @@ static int sla_check_failed_station(const struct sla_station *station)
                        continue;
                if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
                        AST_LIST_REMOVE_CURRENT(entry);
-                       ast_free(failed_station);
+                       sla_failed_station_destroy(failed_station);
                        break;
                }
                res = 1;
@@ -6253,11 +6354,9 @@ static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_
        if (res != AST_DIAL_RESULT_TRYING) {
                struct sla_failed_station *failed_station;
                ast_dial_destroy(dial);
-               if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
-                       return -1;
-               failed_station->station = station;
-               failed_station->last_try = ast_tvnow();
-               AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+               if ((failed_station = sla_create_failed_station(station))) {
+                       AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+               }
                return -1;
        }
        if (!(ringing_station = sla_create_ringing_station(station))) {
@@ -6297,6 +6396,8 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
                        break;
        }
 
+       ao2_ref(trunk_ref, 1);
+
        return trunk_ref;
 }
 
@@ -6308,7 +6409,7 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
 static int sla_check_station_delay(struct sla_station *station, 
        struct sla_ringing_trunk *ringing_trunk)
 {
-       struct sla_trunk_ref *trunk_ref;
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
        unsigned int delay = UINT_MAX;
        int time_left, time_elapsed;
 
@@ -6401,7 +6502,7 @@ static void sla_hangup_stations(void)
                        ast_dial_join(ringing_station->station->dial);
                        ast_dial_destroy(ringing_station->station->dial);
                        ringing_station->station->dial = NULL;
-                       ast_free(ringing_station);
+                       sla_ringing_station_destroy(ringing_station);
                }
        }
        AST_LIST_TRAVERSE_SAFE_END
@@ -6558,8 +6659,10 @@ static int sla_calc_station_delays(unsigned int *timeout)
 {
        struct sla_station *station;
        int res = 0;
+       struct ao2_iterator i;
 
-       AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+       i = ao2_iterator_init(sla_stations, 0);
+       for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
                struct sla_ringing_trunk *ringing_trunk;
                int time_left;
 
@@ -6589,6 +6692,7 @@ static int sla_calc_station_delays(unsigned int *timeout)
                if (time_left < *timeout)
                        *timeout = time_left;
        }
+       ao2_iterator_destroy(&i);
 
        return res;
 }
@@ -6630,47 +6734,19 @@ static int sla_process_timers(struct timespec *ts)
        return 1;
 }
 
-static int sla_load_config(int reload);
-
-/*!
- * \internal
- * \brief Check if we can do a reload of SLA, and do it if we can
- * \pre sla.lock is locked.
- */
-static void sla_check_reload(void)
+static void sla_event_destroy(struct sla_event *event)
 {
-       struct sla_station *station;
-       struct sla_trunk *trunk;
-
-       if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
-               || !AST_LIST_EMPTY(&sla.ringing_stations) || !AST_LIST_EMPTY(&sla.failed_stations)) {
-               return;
+       if (event->trunk_ref) {
+               ao2_ref(event->trunk_ref, -1);
+               event->trunk_ref = NULL;
        }
 
-       AST_RWLIST_RDLOCK(&sla_stations);
-       AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
-               if (station->ref_count)
-                       break;
-       }
-       AST_RWLIST_UNLOCK(&sla_stations);
-       if (station) {
-               return;
-       }
-
-       AST_RWLIST_RDLOCK(&sla_trunks);
-       AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-               if (trunk->ref_count || trunk->chan || trunk->active_stations || trunk->hold_stations) {
-                       break;
-               }
-       }
-       AST_RWLIST_UNLOCK(&sla_trunks);
-       if (trunk) {
-               return;
+       if (event->station) {
+               ao2_ref(event->station, -1);
+               event->station = NULL;
        }
 
-       /* yay */
-       sla_load_config(1);
-       sla.reload = 0;
+       ast_free(event);
 }
 
 static void *sla_thread(void *data)
@@ -6709,27 +6785,21 @@ static void *sla_thread(void *data)
                        case SLA_EVENT_RINGING_TRUNK:
                                sla_handle_ringing_trunk_event();
                                break;
-                       case SLA_EVENT_RELOAD:
-                               sla.reload = 1;
-                       case SLA_EVENT_CHECK_RELOAD:
-                               break;
                        }
-                       ast_free(event);
+                       sla_event_destroy(event);
                        ast_mutex_lock(&sla.lock);
                }
-
-               if (sla.reload) {
-                       sla_check_reload();
-               }
        }
 
        ast_mutex_unlock(&sla.lock);
 
-       while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
-               ast_free(ringing_station);
+       while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
+               sla_ringing_station_destroy(ringing_station);
+       }
 
-       while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
-               ast_free(failed_station);
+       while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
+               sla_failed_station_destroy(failed_station);
+       }
 
        return NULL;
 }
@@ -6750,7 +6820,8 @@ static void *dial_trunk(void *data)
        char conf_name[MAX_CONFNUM];
        struct ast_conference *conf;
        struct ast_flags64 conf_flags = { 0 };
-       struct sla_trunk_ref *trunk_ref = args->trunk_ref;
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
+       RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
        int caller_is_saved;
        struct ast_party_caller caller;
        int last_state = 0;
@@ -6822,8 +6893,8 @@ static void *dial_trunk(void *data)
                        break;
 
                /* check that SLA station that originated trunk call is still alive */
-               if (args->station && ast_device_state(args->station->device) == AST_DEVICE_NOT_INUSE) {
-                       ast_debug(3, "Originating station device %s no longer active\n", args->station->device);
+               if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
+                       ast_debug(3, "Originating station device %s no longer active\n", station->device);
                        trunk_ref->trunk->chan = NULL;
                        break;
                }
@@ -6876,15 +6947,19 @@ static void *dial_trunk(void *data)
        return NULL;
 }
 
-/*! \brief For a given station, choose the highest priority idle trunk
+/*!
+ * \brief For a given station, choose the highest priority idle trunk
+ * \pre sla_station is locked
  */
 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
 {
        struct sla_trunk_ref *trunk_ref = NULL;
 
        AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-               if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
+               if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
+                       ao2_ref(trunk_ref, 1);
                        break;
+               }
        }
 
        return trunk_ref;
@@ -6893,8 +6968,8 @@ static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *sta
 static int sla_station_exec(struct ast_channel *chan, const char *data)
 {
        char *station_name, *trunk_name;
-       struct sla_station *station;
-       struct sla_trunk_ref *trunk_ref = NULL;
+       RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
        char conf_name[MAX_CONFNUM];
        struct ast_flags64 conf_flags = { 0 };
        struct ast_conference *conf;
@@ -6914,25 +6989,21 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                return 0;
        }
 
-       AST_RWLIST_WRLOCK(&sla_stations);
        station = sla_find_station(station_name);
-       if (station)
-               ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
-       AST_RWLIST_UNLOCK(&sla_stations);
 
        if (!station) {
                ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
                pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);
                return 0;
        }
 
-       AST_RWLIST_RDLOCK(&sla_trunks);
+       ao2_lock(station);
        if (!ast_strlen_zero(trunk_name)) {
                trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
-       } else
+       } else {
                trunk_ref = sla_choose_idle_trunk(station);
-       AST_RWLIST_UNLOCK(&sla_trunks);
+       }
+       ao2_unlock(station);
 
        if (!trunk_ref) {
                if (ast_strlen_zero(trunk_name))
@@ -6942,8 +7013,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                                "'%s' due to access controls.\n", trunk_name);
                }
                pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
-               ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);
                return 0;
        }
 
@@ -6972,7 +7041,7 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        answer_trunk_chan(ringing_trunk->trunk->chan);
                        sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
 
-                       free(ringing_trunk);
+                       sla_ringing_trunk_destroy(ringing_trunk);
 
                        /* Queue up reprocessing ringing trunks, and then ringing stations again */
                        sla_queue_event(SLA_EVENT_RINGING_TRUNK);
@@ -6992,6 +7061,8 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        .cond_lock = &cond_lock,
                        .cond = &cond,
                };
+               ao2_ref(trunk_ref, 1);
+               ao2_ref(station, 1);
                sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
                /* Create a thread to dial the trunk and dump it into the conference.
                 * However, we want to wait until the trunk has been dialed and the
@@ -7011,8 +7082,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
                        sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
                        trunk_ref->chan = NULL;
-                       ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-                       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
                        return 0;
                }
        }
@@ -7045,19 +7114,28 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
        
        pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
 
-       ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
        return 0;
 }
 
+static void sla_trunk_ref_destructor(void *obj)
+{
+       struct sla_trunk_ref *trunk_ref = obj;
+
+       if (trunk_ref->trunk) {
+               ao2_ref(trunk_ref->trunk, -1);
+               trunk_ref->trunk = NULL;
+       }
+}
+
 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
 {
        struct sla_trunk_ref *trunk_ref;
 
-       if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
+       if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
                return NULL;
+       }
 
+       ao2_ref(trunk, 1);
        trunk_ref->trunk = trunk;
 
        return trunk_ref;
@@ -7067,9 +7145,11 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
 {
        struct sla_ringing_trunk *ringing_trunk;
 
-       if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
+       if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
                return NULL;
-       
+       }
+
+       ao2_ref(trunk, 1);
        ringing_trunk->trunk = trunk;
        ringing_trunk->ring_begin = ast_tvnow();
 
@@ -7084,6 +7164,16 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
        return ringing_trunk;
 }
 
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
+{
+       if (ringing_trunk->trunk) {
+               ao2_ref(ringing_trunk->trunk, -1);
+               ringing_trunk->trunk = NULL;
+       }
+
+       ast_free(ringing_trunk);
+}
+
 enum {
        SLA_TRUNK_OPT_MOH = (1 << 0),
 };
@@ -7102,7 +7192,7 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        char conf_name[MAX_CONFNUM];
        struct ast_conference *conf;
        struct ast_flags64 conf_flags = { 0 };
-       struct sla_trunk *trunk;
+       RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
        struct sla_ringing_trunk *ringing_trunk;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(trunk_name);
@@ -7126,16 +7216,11 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
                }
        }
 
-       AST_RWLIST_WRLOCK(&sla_trunks);
        trunk = sla_find_trunk(args.trunk_name);
-       if (trunk)
-               ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
-       AST_RWLIST_UNLOCK(&sla_trunks);
 
        if (!trunk) {
                ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
                pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);        
                return 0;
        }
 
@@ -7143,8 +7228,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
                ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
                        args.trunk_name);
                pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-               ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);        
                return 0;
        }
 
@@ -7152,8 +7235,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
 
        if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
                pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-               ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);        
                return 0;
        }
 
@@ -7161,8 +7242,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
        if (!conf) {
                pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-               ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-               sla_queue_event(SLA_EVENT_CHECK_RELOAD);        
                return 0;
        }
        ast_set_flag64(&conf_flags, 
@@ -7196,46 +7275,37 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        AST_LIST_TRAVERSE_SAFE_END;
        ast_mutex_unlock(&sla.lock);
        if (ringing_trunk) {
-               ast_free(ringing_trunk);
+               sla_ringing_trunk_destroy(ringing_trunk);
                pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
                /* Queue reprocessing of ringing trunks to make stations stop ringing
                 * that shouldn't be ringing after this trunk stopped. */
                sla_queue_event(SLA_EVENT_RINGING_TRUNK);
        }
 
-       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-       sla_queue_event(SLA_EVENT_CHECK_RELOAD);        
-
        return 0;
 }
 
 static enum ast_device_state sla_state(const char *data)
 {
        char *buf, *station_name, *trunk_name;
-       struct sla_station *station;
+       RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
        struct sla_trunk_ref *trunk_ref;
        enum ast_device_state res = AST_DEVICE_INVALID;
 
        trunk_name = buf = ast_strdupa(data);
        station_name = strsep(&trunk_name, "_");
 
-       AST_RWLIST_RDLOCK(&sla_stations);
-       AST_LIST_TRAVERSE(&sla_stations, station, entry) {
-               if (strcasecmp(station_name, station->name))
-                       continue;
-               AST_RWLIST_RDLOCK(&sla_trunks);
+       station = sla_find_station(station_name);
+       if (station) {
+               ao2_lock(station);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-                       if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
+                       if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
+                               res = sla_state_to_devstate(trunk_ref->state);
                                break;
+                       }
                }
-               if (!trunk_ref) {
-                       AST_RWLIST_UNLOCK(&sla_trunks);
-                       break;
-               }
-               res = sla_state_to_devstate(trunk_ref->state);
-               AST_RWLIST_UNLOCK(&sla_trunks);
+               ao2_unlock(station);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
 
        if (res == AST_DEVICE_INVALID) {
                ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
@@ -7245,26 +7315,39 @@ static enum ast_device_state sla_state(const char *data)
        return res;
 }
 
-static void destroy_trunk(struct sla_trunk *trunk)
+static int sla_trunk_release_refs(void *obj, void *arg, int flags)
 {
+       struct sla_trunk *trunk = obj;
        struct sla_station_ref *station_ref;
 
-       if (!ast_strlen_zero(trunk->autocontext))
-               ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
-
-       while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
-               ast_free(station_ref);
+       while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
+               ao2_ref(station_ref, -1);
+       }
 
-       ast_string_field_free_memory(trunk);
-       ast_free(trunk);
+       return 0;
 }
 
-static void destroy_station(struct sla_station *station)
+static int sla_station_release_refs(void *obj, void *arg, int flags)
 {
+       struct sla_station *station = obj;
        struct sla_trunk_ref *trunk_ref;
 
+       while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
+               ao2_ref(trunk_ref, -1);
+       }
+
+       return 0;
+}
+
+static void sla_station_destructor(void *obj)
+{
+       struct sla_station *station = obj;
+
+       ast_debug(1, "sla_station destructor for '%s'\n", station->name);
+
        if (!ast_strlen_zero(station->autocontext)) {
-               AST_RWLIST_RDLOCK(&sla_trunks);
+               struct sla_trunk_ref *trunk_ref;
+
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
                        char exten[AST_MAX_EXTENSION];
                        char hint[AST_MAX_APP];
@@ -7275,31 +7358,43 @@ static void destroy_station(struct sla_station *station)
                        ast_context_remove_extension(station->autocontext, hint, 
                                PRIORITY_HINT, sla_registrar);
                }
-               AST_RWLIST_UNLOCK(&sla_trunks);
        }
 
-       while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
-               ast_free(trunk_ref);
+       sla_station_release_refs(station, NULL, 0);
 
        ast_string_field_free_memory(station);
-       ast_free(station);
 }
 
-static void sla_destroy(void)
+static int sla_trunk_hash(const void *obj, const int flags)
 {
-       struct sla_trunk *trunk;
-       struct sla_station *station;
+       const struct sla_trunk *trunk = obj;
+
+       return ast_str_case_hash(trunk->name);
+}
 
-       AST_RWLIST_WRLOCK(&sla_trunks);
-       while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
-               destroy_trunk(trunk);
-       AST_RWLIST_UNLOCK(&sla_trunks);
+static int sla_trunk_cmp(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj, *trunk2 = arg;
 
-       AST_RWLIST_WRLOCK(&sla_stations);
-       while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
-               destroy_station(station);
-       AST_RWLIST_UNLOCK(&sla_stations);
+       return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int sla_station_hash(const void *obj, const int flags)
+{
+       const struct sla_station *station = obj;
 
+       return ast_str_case_hash(station->name);
+}
+
+static int sla_station_cmp(void *obj, void *arg, int flags)
+{
+       struct sla_station *station = obj, *station2 = arg;
+
+       return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void sla_destroy(void)
+{
        if (sla.thread != AST_PTHREADT_NULL) {
                ast_mutex_lock(&sla.lock);
                sla.stop = 1;
@@ -7313,6 +7408,15 @@ static void sla_destroy(void)
 
        ast_mutex_destroy(&sla.lock);
        ast_cond_destroy(&sla.cond);
+
+       ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
+       ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
+
+       ao2_ref(sla_trunks, -1);
+       sla_trunks = NULL;
+
+       ao2_ref(sla_stations, -1);
+       sla_stations = NULL;
 }
 
 static int sla_check_device(const char *device)
@@ -7328,11 +7432,27 @@ static int sla_check_device(const char *device)
        return 0;
 }
 
+static void sla_trunk_destructor(void *obj)
+{
+       struct sla_trunk *trunk = obj;
+
+       ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
+
+       if (!ast_strlen_zero(trunk->autocontext)) {
+               ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
+       }
+
+       sla_trunk_release_refs(trunk, NULL, 0);
+
+       ast_string_field_free_memory(trunk);
+}
+
 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
 {
-       struct sla_trunk *trunk;
+       RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
        struct ast_variable *var;
        const char *dev;
+       int existing_trunk = 0;
 
        if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
                ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
@@ -7340,16 +7460,25 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
        }
 
        if (sla_check_device(dev)) {
-               ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
+               ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
                        cat, dev);
                return -1;
        }
 
-       if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
+       if ((trunk = sla_find_trunk(cat))) {
+               trunk->mark = 0;
+               existing_trunk = 1;
+       } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
+               if (ast_string_field_init(trunk, 32)) {
+                       return -1;
+               }
+               ast_string_field_set(trunk, name, cat);
+       } else {
                return -1;
        }
 
-       ast_string_field_set(trunk, name, cat);
+       ao2_lock(trunk);
+
        ast_string_field_set(trunk, device, dev);
 
        for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
@@ -7378,54 +7507,65 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
                }
        }
 
+       ao2_unlock(trunk);
+
        if (!ast_strlen_zero(trunk->autocontext)) {
                struct ast_context *context;
                context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
                if (!context) {
                        ast_log(LOG_ERROR, "Failed to automatically find or create "
                                "context '%s' for SLA!\n", trunk->autocontext);
-                       destroy_trunk(trunk);
                        return -1;
                }
                if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
                        NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically create extension "
                                "for trunk '%s'!\n", trunk->name);
-                       destroy_trunk(trunk);
                        return -1;
                }
        }
 
-       AST_RWLIST_WRLOCK(&sla_trunks);
-       AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
-       AST_RWLIST_UNLOCK(&sla_trunks);
+       if (!existing_trunk) {
+               ao2_link(sla_trunks, trunk);
+       }
 
        return 0;
 }
 
+/*!
+ * \internal
+ * \pre station is not locked
+ */
 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
 {
-       struct sla_trunk *trunk;
-       struct sla_trunk_ref *trunk_ref;
+       RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
+       struct sla_trunk_ref *trunk_ref = NULL;
        struct sla_station_ref *station_ref;
        char *trunk_name, *options, *cur;
+       int existing_trunk_ref = 0;
+       int existing_station_ref = 0;
 
        options = ast_strdupa(var->value);
        trunk_name = strsep(&options, ",");
-       
-       AST_RWLIST_RDLOCK(&sla_trunks);
-       AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-               if (!strcasecmp(trunk->name, trunk_name))
-                       break;
-       }
 
-       AST_RWLIST_UNLOCK(&sla_trunks);
+       trunk = sla_find_trunk(trunk_name);
        if (!trunk) {
                ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
                return;
        }
-       if (!(trunk_ref = create_trunk_ref(trunk)))
+
+       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+               if (trunk_ref->trunk == trunk) {
+                       trunk_ref->mark = 0;
+                       existing_trunk_ref = 1;
+                       break;
+               }
+       }
+
+       if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
                return;
+       }
+
        trunk_ref->state = SLA_TRUNK_STATE_IDLE;
 
        while ((cur = strsep(&options, ","))) {
@@ -7449,41 +7589,73 @@ static void sla_add_trunk_to_station(struct sla_station *station, struct ast_var
                }
        }
 
-       if (!(station_ref = sla_create_station_ref(station))) {
-               ast_free(trunk_ref);
+       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+               if (station_ref->station == station) {
+                       station_ref->mark = 0;
+                       existing_station_ref = 1;
+                       break;
+               }
+       }
+
+       if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
+               if (!existing_trunk_ref) {
+                       ao2_ref(trunk_ref, -1);
+               } else {
+                       trunk_ref->mark = 1;
+               }
                return;
        }
-       ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
-       AST_RWLIST_WRLOCK(&sla_trunks);
-       AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
-       AST_RWLIST_UNLOCK(&sla_trunks);
-       AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+
+       if (!existing_station_ref) {
+               ao2_lock(trunk);
+               AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
+               ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
+               ao2_unlock(trunk);
+       }
+
+       if (!existing_trunk_ref) {
+               ao2_lock(station);
+               AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+               ao2_unlock(station);
+       }
 }
 
 static int sla_build_station(struct ast_config *cfg, const char *cat)
 {
-       struct sla_station *station;
+       RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
        struct ast_variable *var;
        const char *dev;
+       int existing_station = 0;
 
        if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
                ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
                return -1;
        }
 
-       if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
+       if ((station = sla_find_station(cat))) {
+               station->mark = 0;
+               existing_station = 1;
+       } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
+               if (ast_string_field_init(station, 32)) {
+                       return -1;
+               }
+               ast_string_field_set(station, name, cat);
+       } else {
                return -1;
        }
 
-       ast_string_field_set(station, name, cat);
+       ao2_lock(station);
+
        ast_string_field_set(station, device, dev);
 
        for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
-               if (!strcasecmp(var->name, "trunk"))
+               if (!strcasecmp(var->name, "trunk")) {
+                       ao2_unlock(station);
                        sla_add_trunk_to_station(station, var);
-               else if (!strcasecmp(var->name, "autocontext"))
+                       ao2_lock(station);
+               } else if (!strcasecmp(var->name, "autocontext")) {
                        ast_string_field_set(station, autocontext, var->value);
-               else if (!strcasecmp(var->name, "ringtimeout")) {
+               } else if (!strcasecmp(var->name, "ringtimeout")) {
                        if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
                                ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
                                        var->value, station->name);
@@ -7511,6 +7683,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                }
        }
 
+       ao2_unlock(station);
+
        if (!ast_strlen_zero(station->autocontext)) {
                struct ast_context *context;
                struct sla_trunk_ref *trunk_ref;
@@ -7518,7 +7692,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                if (!context) {
                        ast_log(LOG_ERROR, "Failed to automatically find or create "
                                "context '%s' for SLA!\n", station->autocontext);
-                       destroy_station(station);
                        return -1;
                }
                /* The extension for when the handset goes off-hook.
@@ -7527,10 +7700,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                        NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically create extension "
                                "for trunk '%s'!\n", station->name);
-                       destroy_station(station);
                        return -1;
                }
-               AST_RWLIST_RDLOCK(&sla_trunks);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
                        char exten[AST_MAX_EXTENSION];
                        char hint[AST_MAX_APP];
@@ -7542,7 +7713,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                                NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create extension "
                                        "for trunk '%s'!\n", station->name);
-                               destroy_station(station);
                                return -1;
                        }
                        /* Hint for this line button 
@@ -7551,20 +7721,115 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                                NULL, NULL, hint, NULL, NULL, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create hint "
                                        "for trunk '%s'!\n", station->name);
-                               destroy_station(station);
                                return -1;
                        }
                }
-               AST_RWLIST_UNLOCK(&sla_trunks);
        }
 
-       AST_RWLIST_WRLOCK(&sla_stations);
-       AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
-       AST_RWLIST_UNLOCK(&sla_stations);
+       if (!existing_station) {
+               ao2_link(sla_stations, station);
+       }
+
+       return 0;
+}
+
+static int sla_trunk_mark(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj;
+       struct sla_station_ref *station_ref;
+
+       ao2_lock(trunk);
+
+       trunk->mark = 1;
+
+       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+               station_ref->mark = 1;
+       }
+
+       ao2_unlock(trunk);
+
+       return 0;
+}
+
+static int sla_station_mark(void *obj, void *arg, int flags)
+{
+       struct sla_station *station = obj;
+       struct sla_trunk_ref *trunk_ref;
+
+       ao2_lock(station);
+
+       station->mark = 1;
+
+       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+               trunk_ref->mark = 1;
+       }
+
+       ao2_unlock(station);
 
        return 0;
 }
 
+static int sla_trunk_is_marked(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj;
+
+       ao2_lock(trunk);
+
+       if (trunk->mark) {
+               /* Only remove all of the station references if the trunk itself is going away */
+               sla_trunk_release_refs(trunk, NULL, 0);
+       } else {
+               struct sla_station_ref *station_ref;
+
+               /* Otherwise only remove references to stations no longer in the config */
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
+                       if (!station_ref->mark) {
+                               continue;
+                       }
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ao2_ref(station_ref, -1);
+               }
+               AST_LIST_TRAVERSE_SAFE_END
+       }
+
+       ao2_unlock(trunk);
+
+       return trunk->mark ? CMP_MATCH : 0;
+}
+
+static int sla_station_is_marked(void *obj, void *arg, int flags)
+{
+       struct sla_station *station = obj;
+
+       ao2_lock(station);
+
+       if (station->mark) {
+               /* Only remove all of the trunk references if the station itself is going away */
+               sla_station_release_refs(station, NULL, 0);
+       } else {
+               struct sla_trunk_ref *trunk_ref;
+
+               /* Otherwise only remove references to trunks no longer in the config */
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
+                       if (!trunk_ref->mark) {
+                               continue;
+                       }
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ao2_ref(trunk_ref, -1);
+               }
+               AST_LIST_TRAVERSE_SAFE_END
+       }
+
+       ao2_unlock(station);
+
+       return station->mark ? CMP_MATCH : 0;
+}
+
+static int sla_in_use(void)
+{
+       return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
+}
+
 static int sla_load_config(int reload)
 {
        struct ast_config *cfg;
@@ -7576,6 +7841,8 @@ static int sla_load_config(int reload)
        if (!reload) {
                ast_mutex_init(&sla.lock);
                ast_cond_init(&sla.cond, NULL);
+               sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
+               sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
        }
 
        if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
@@ -7588,21 +7855,8 @@ static int sla_load_config(int reload)
        }
 
        if (reload) {
-               struct sla_station *station;
-               struct sla_trunk *trunk;
-
-               /* We need to actually delete the previous versions of trunks and stations now */
-               AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
-                       AST_RWLIST_REMOVE_CURRENT(entry);
-                       ast_free(station);
-               }
-               AST_RWLIST_TRAVERSE_SAFE_END;
-
-               AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
-                       AST_RWLIST_REMOVE_CURRENT(entry);
-                       ast_free(trunk);
-               }
-               AST_RWLIST_TRAVERSE_SAFE_END;
+               ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
+               ao2_callback(sla_stations, 0, sla_station_mark, NULL);
        }
 
        if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
@@ -7629,9 +7883,13 @@ static int sla_load_config(int reload)
 
        ast_config_destroy(cfg);
 
-       /* Even if we don't have any stations, we may after a reload and we need to
-        * be able to process the SLA_EVENT_RELOAD event in that case */
-       if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
+       if (reload) {
+               ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
+               ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
+       }
+
+       /* Start SLA event processing thread once SLA has been configured. */
+       if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
                ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
        }
 
@@ -7716,15 +7974,7 @@ static struct ast_custom_function meetme_info_acf = {
 static int load_config(int reload)
 {
        load_config_meetme();
-
-       if (reload && sla.thread != AST_PTHREADT_NULL) {
-               sla_queue_event(SLA_EVENT_RELOAD);
-               ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
-                       "and will be completed when the system is idle.\n");
-               return 0;
-       }
-       
-       return sla_load_config(0);
+       return sla_load_config(reload);
 }
 
 #define MEETME_DATA_EXPORT(MEMBER)                                     \