diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index 49b2a05f20c39032fb0484bc7847ea08a0d61be5..738b773cb33cc5f2a528e5217e330610f894c64a 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -390,12 +390,24 @@ murf
  */
 typedef void (*ao2_destructor_fn)(void *);
 
+/*! \brief Options available when allocating an ao2 object. */
+enum ao2_alloc_opts {
+	/*! The ao2 object has a recursive mutex lock associated with it. */
+	AO2_ALLOC_OPT_LOCK_MUTEX = (0 << 0),
+	/*! The ao2 object has a non-recursive read/write lock associated with it. */
+	AO2_ALLOC_OPT_LOCK_RWLOCK = (1 << 0),
+	/*! The ao2 object has no lock associated with it. */
+	AO2_ALLOC_OPT_LOCK_NOLOCK = (2 << 0),
+	/*! The ao2 object locking option field mask. */
+	AO2_ALLOC_OPT_LOCK_MASK = (3 << 0),
+};
 
 /*!
  * \brief Allocate and initialize an object.
  *
  * \param data_size The sizeof() of the user-defined structure.
  * \param destructor_fn The destructor function (can be NULL)
+ * \param options The ao2 object options (See enum ao2_alloc_opts)
  * \param debug_msg An ao2 object debug tracing message.
  * \return A pointer to user-data.
  *
@@ -413,30 +425,45 @@ typedef void (*ao2_destructor_fn)(void *);
 
 #if defined(REF_DEBUG)
 
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+	__ao2_alloc_debug((data_size), (destructor_fn), (options), (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+	__ao2_alloc_debug((data_size), (destructor_fn), (options), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
 #define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
-	__ao2_alloc_debug((data_size), (destructor_fn), (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+	__ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
 #define ao2_alloc(data_size, destructor_fn) \
-	__ao2_alloc_debug((data_size), (destructor_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+	__ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
 
 #elif defined(__AST_DEBUG_MALLOC)
 
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+	__ao2_alloc_debug((data_size), (destructor_fn), (options), (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+	__ao2_alloc_debug((data_size), (destructor_fn), (options), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
 #define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
-	__ao2_alloc_debug((data_size), (destructor_fn), (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+	__ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
 #define ao2_alloc(data_size, destructor_fn) \
-	__ao2_alloc_debug((data_size), (destructor_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+	__ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
 
 #else
 
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+	__ao2_alloc((data_size), (destructor_fn), (options))
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+	__ao2_alloc((data_size), (destructor_fn), (options))
+
 #define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
-	__ao2_alloc((data_size), (destructor_fn))
+	__ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX)
 #define ao2_alloc(data_size, destructor_fn) \
-	__ao2_alloc((data_size), (destructor_fn))
+	__ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX)
 
 #endif
 
-void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const char *tag,
+void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
 	const char *file, int line, const char *funcname, int ref_debug);
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn);
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options);
 
 /*! @} */
 
@@ -476,19 +503,31 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn);
 
 #endif
 
-int __ao2_ref_debug(void *o, int delta, const char *tag, char *file, int line, const char *funcname);
+int __ao2_ref_debug(void *o, int delta, const char *tag, const char *file, int line, const char *funcname);
 int __ao2_ref(void *o, int delta);
 
 /*! @} */
 
+/*! \brief Which lock to request. */
+enum ao2_lock_req {
+	/*! Request the mutex lock be acquired. */
+	AO2_LOCK_REQ_MUTEX,
+	/*! Request the read lock be acquired. */
+	AO2_LOCK_REQ_RDLOCK,
+	/*! Request the write lock be acquired. */
+	AO2_LOCK_REQ_WRLOCK,
+};
+
 /*! \brief
  * Lock an object.
  *
  * \param a A pointer to the object we want to lock.
  * \return 0 on success, other values on error.
  */
-int __ao2_lock(void *a, const char *file, const char *func, int line, const char *var);
-#define ao2_lock(a) __ao2_lock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+int __ao2_lock(void *a, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var);
+#define ao2_lock(a) __ao2_lock(a, AO2_LOCK_REQ_MUTEX, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_rdlock(a) __ao2_lock(a, AO2_LOCK_REQ_RDLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_wrlock(a) __ao2_lock(a, AO2_LOCK_REQ_WRLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
 
 /*! \brief
  * Unlock an object.
@@ -505,8 +544,10 @@ int __ao2_unlock(void *a, const char *file, const char *func, int line, const ch
  * \param a A pointer to the object we want to lock.
  * \return 0 on success, other values on error.
  */
-int __ao2_trylock(void *a, const char *file, const char *func, int line, const char *var);
-#define ao2_trylock(a) __ao2_trylock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+int __ao2_trylock(void *a, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var);
+#define ao2_trylock(a) __ao2_trylock(a, AO2_LOCK_REQ_MUTEX, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_tryrdlock(a) __ao2_trylock(a, AO2_LOCK_REQ_RDLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_trywrlock(a) __ao2_trylock(a, AO2_LOCK_REQ_WRLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
 
 /*!
  * \brief Return the mutex lock address of an object
@@ -693,9 +734,17 @@ enum search_flags {
 	 */
 	OBJ_CONTINUE = (1 << 4),
 	/*!
-	 * \brief By using this flag, the ao2_container being searched will _NOT_
-	 * be locked.  Only use this flag if the ao2_container is being protected
-	 * by another mechanism other that the internal ao2_lock.
+	 * \brief Assume that the ao2_container is already locked.
+	 *
+	 * \note For ao2_containers that have mutexes, no locking will
+	 * be done.
+	 *
+	 * \note For ao2_containers that have RWLOCKs, the lock will be
+	 * promoted to write mode as needed.  The lock will be returned
+	 * to the original locked state.
+	 *
+	 * \note Only use this flag if the ao2_container is manually
+	 * locked already.
 	 */
 	OBJ_NOLOCK = (1 << 5),
 	/*!
@@ -732,6 +781,7 @@ struct ao2_container;
  * We allocate space for a struct astobj_container, struct container
  * and the buckets[] array.
  *
+ * \param options Container ao2 object options (See enum ao2_alloc_opts)
  * \param n_buckets Number of buckets for hash
  * \param hash_fn Pointer to a function computing a hash value.
  * \param cmp_fn Pointer to a compare function used by ao2_find. (NULL to match everything)
@@ -744,32 +794,47 @@ struct ao2_container;
 
 #if defined(REF_DEBUG)
 
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+	__ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+	__ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
 #define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
-	__ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+	__ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
 #define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
-	__ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+	__ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
 
 #elif defined(__AST_DEBUG_MALLOC)
 
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+	__ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+	__ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
 #define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
-	__ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+	__ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
 #define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
-	__ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+	__ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
 
 #else
 
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+	__ao2_container_alloc((options), (n_buckets), (hash_fn), (cmp_fn))
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+	__ao2_container_alloc((options), (n_buckets), (hash_fn), (cmp_fn))
+
 #define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
-	__ao2_container_alloc((n_buckets), (hash_fn), (cmp_fn))
+	__ao2_container_alloc(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn))
 #define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
-	__ao2_container_alloc((n_buckets), (hash_fn), (cmp_fn))
+	__ao2_container_alloc(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn))
 
 #endif
 
-struct ao2_container *__ao2_container_alloc(unsigned int n_buckets, ao2_hash_fn *hash_fn,
-	ao2_callback_fn *cmp_fn);
-struct ao2_container *__ao2_container_alloc_debug(unsigned int n_buckets,
-	ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
-	const char *tag, char *file, int line, const char *funcname, int ref_debug);
+struct ao2_container *__ao2_container_alloc(unsigned int options,
+	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
+struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
+	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
+	const char *tag, const char *file, int line, const char *funcname, int ref_debug);
 
 /*! \brief
  * Returns the number of elements in a container.
@@ -810,7 +875,7 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
  * \retval NULL on error.
  */
 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags);
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, char *file, int line, const char *funcname, int ref_debug);
+struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *funcname, int ref_debug);
 #if defined(REF_DEBUG)
 
 #define ao2_t_container_clone(orig, flags, tag)	__ao2_container_clone_debug(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
@@ -843,6 +908,7 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
  *
  * \param container The container to operate on.
  * \param obj The object to be added.
+ * \param flags search_flags to control linking the object.  (OBJ_NOLOCK)
  * \param tag used for debugging.
  *
  * \retval NULL on errors.
@@ -860,20 +926,20 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
 #define ao2_t_link(container, obj, tag)					__ao2_link_debug((container), (obj), 0, (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 #define ao2_link(container, obj)						__ao2_link_debug((container), (obj), 0, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
-#define ao2_t_link_nolock(container, obj, tag)			__ao2_link_debug((container), (obj), OBJ_NOLOCK, (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_link_nolock(container, obj)					__ao2_link_debug((container), (obj), OBJ_NOLOCK, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_t_link_flags(container, obj, flags, tag)	__ao2_link_debug((container), (obj), (flags), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_link_flags(container, obj, flags)			__ao2_link_debug((container), (obj), (flags), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
 #else
 
 #define ao2_t_link(container, obj, tag)					__ao2_link((container), (obj), 0)
 #define ao2_link(container, obj)						__ao2_link((container), (obj), 0)
 
-#define ao2_t_link_nolock(container, obj, tag)			__ao2_link((container), (obj), OBJ_NOLOCK)
-#define ao2_link_nolock(container, obj)					__ao2_link((container), (obj), OBJ_NOLOCK)
+#define ao2_t_link_flags(container, obj, flags, tag)	__ao2_link((container), (obj), (flags))
+#define ao2_link_flags(container, obj, flags)			__ao2_link((container), (obj), (flags))
 
 #endif
 
-void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname);
 void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
 
 /*!
@@ -881,6 +947,7 @@ void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
  *
  * \param container The container to operate on.
  * \param obj The object to unlink.
+ * \param flags search_flags to control unlinking the object.  (OBJ_NOLOCK)
  * \param tag used for debugging.
  *
  * \retval NULL, always
@@ -898,20 +965,20 @@ void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
 #define ao2_t_unlink(container, obj, tag)				__ao2_unlink_debug((container), (obj), 0, (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 #define ao2_unlink(container, obj)						__ao2_unlink_debug((container), (obj), 0, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
-#define ao2_t_unlink_nolock(container, obj, tag)		__ao2_unlink_debug((container), (obj), OBJ_NOLOCK, (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_unlink_nolock(container, obj)				__ao2_unlink_debug((container), (obj), OBJ_NOLOCK, "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_t_unlink_flags(container, obj, flags, tag)	__ao2_unlink_debug((container), (obj), (flags), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_unlink_flags(container, obj, flags)			__ao2_unlink_debug((container), (obj), (flags), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
 #else
 
 #define ao2_t_unlink(container, obj, tag)				__ao2_unlink((container), (obj), 0)
 #define ao2_unlink(container, obj)						__ao2_unlink((container), (obj), 0)
 
-#define ao2_t_unlink_nolock(container, obj, tag)		__ao2_unlink((container), (obj), OBJ_NOLOCK)
-#define ao2_unlink_nolock(container, obj)				__ao2_unlink((container), (obj), OBJ_NOLOCK)
+#define ao2_t_unlink_flags(container, obj, flags, tag)	__ao2_unlink((container), (obj), (flags))
+#define ao2_unlink_flags(container, obj, flags)			__ao2_unlink((container), (obj), (flags))
 
 #endif
 
-void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, const char *file, int line, const char *funcname);
 void *__ao2_unlink(struct ao2_container *c, void *obj, int flags);
 
 
@@ -1010,7 +1077,7 @@ void *__ao2_unlink(struct ao2_container *c, void *obj, int flags);
 #endif
 
 void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
-	ao2_callback_fn *cb_fn, void *arg, const char *tag, char *file, int line,
+	ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
 	const char *funcname);
 void *__ao2_callback(struct ao2_container *c, enum search_flags flags, ao2_callback_fn *cb_fn, void *arg);
 
@@ -1048,7 +1115,7 @@ void *__ao2_callback(struct ao2_container *c, enum search_flags flags, ao2_callb
 #endif
 
 void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
-	ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, char *file,
+	ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
 	int line, const char *funcname);
 void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
 	ao2_callback_data_fn *cb_fn, void *arg, void *data);
@@ -1073,7 +1140,7 @@ void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
 #endif
 
 void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
-	const char *tag, char *file, int line, const char *funcname);
+	const char *tag, const char *file, int line, const char *funcname);
 void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags);
 
 /*! \brief
@@ -1176,8 +1243,18 @@ struct ao2_iterator {
  * of the iterator.
  */
 enum ao2_iterator_flags {
-	/*! Prevents ao2_iterator_next() from locking the container
-	 * while retrieving the next object from it.
+	/*!
+	 * \brief Assume that the ao2_container is already locked.
+	 *
+	 * \note For ao2_containers that have mutexes, no locking will
+	 * be done.
+	 *
+	 * \note For ao2_containers that have RWLOCKs, the lock will be
+	 * promoted to write mode as needed.  The lock will be returned
+	 * to the original locked state.
+	 *
+	 * \note Only use this flag if the ao2_container is manually
+	 * locked already.
 	 */
 	AO2_ITERATOR_DONTLOCK = (1 << 0),
 	/*! Indicates that the iterator was dynamically allocated by
@@ -1234,7 +1311,7 @@ void ao2_iterator_destroy(struct ao2_iterator *i);
 
 #endif
 
-void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname);
 void *__ao2_iterator_next(struct ao2_iterator *a);
 
 /* extra functions */
diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h
index fbcbecccf916a04db3037745af2fd3bad8cb9eda..943c7ffe61fdd1cf9aadbd9e3a40f246463c9013 100644
--- a/include/asterisk/lock.h
+++ b/include/asterisk/lock.h
@@ -352,7 +352,7 @@ int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, in
 			if (__res2) { \
 				ast_log(LOG_WARNING, "Could not unlock channel '%s': %s.  {{{Originally locked at %s line %d: (%s) '%s'}}}  I will NOT try to relock.\n", #chan, strerror(__res2), __filename, __lineno, __func, __mutex_name); \
 			} else { \
-				__ao2_lock(chan, __filename, __func, __lineno, __mutex_name); \
+				__ao2_lock(chan, AO2_LOCK_REQ_MUTEX, __filename, __func, __lineno, __mutex_name); \
 			} \
 		} \
 	} while (0)
diff --git a/main/astobj2.c b/main/astobj2.c
index 62e76dbf6fa69e2565fe0a350270fa078f669259..d0643fe5b9a4a5b90ec7252efc6c5331b8356482 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -34,20 +34,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 /*!
  * astobj2 objects are always preceded by this data structure,
- * which contains a lock, a reference counter,
- * the flags and a pointer to a destructor.
+ * which contains a reference counter,
+ * option flags and a pointer to a destructor.
  * The refcount is used to decide when it is time to
  * invoke the destructor.
  * The magic number is used for consistency check.
- * XXX the lock is not always needed, and its initialization may be
- * expensive. Consider making it external.
  */
 struct __priv_data {
-	ast_mutex_t lock;
 	int ref_counter;
 	ao2_destructor_fn destructor_fn;
-	/*! for stats */
+	/*! User data size for stats */
 	size_t data_size;
+	/*! The ao2 object option flags */
+	uint32_t options;
 	/*! magic number.  This is used to verify that a pointer passed in is a
 	 *  valid astobj2 */
 	uint32_t magic;
@@ -64,9 +63,29 @@ struct astobj2 {
 	void *user_data[0];
 };
 
-#ifdef AST_DEVMODE
-/* #define AO2_DEBUG 1 */
-#endif
+struct ao2_lock_priv {
+	ast_mutex_t lock;
+};
+
+/* AstObj2 with recursive lock. */
+struct astobj2_lock {
+	struct ao2_lock_priv mutex;
+	struct __priv_data priv_data;
+	void *user_data[0];
+};
+
+struct ao2_rwlock_priv {
+	ast_rwlock_t lock;
+	/*! Count of the number of threads holding a lock on this object. -1 if it is the write lock. */
+	int num_lockers;
+};
+
+/* AstObj2 with RW lock. */
+struct astobj2_rwlock {
+	struct ao2_rwlock_priv rwlock;
+	struct __priv_data priv_data;
+	void *user_data[0];
+};
 
 #ifdef AO2_DEBUG
 struct ao2_stats {
@@ -102,6 +121,12 @@ void ao2_bt(void)
 }
 #endif
 
+#define INTERNAL_OBJ_MUTEX(user_data) \
+	((struct astobj2_lock *) (((char *) (user_data)) - sizeof(struct astobj2_lock)))
+
+#define INTERNAL_OBJ_RWLOCK(user_data) \
+	((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
+
 /*!
  * \brief convert from a pointer _p to a user-defined object
  *
@@ -119,7 +144,7 @@ static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
 	p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
 	if (AO2_MAGIC != (p->priv_data.magic) ) {
 		ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
-		p = NULL;
+		return NULL;
 	}
 
 	return p;
@@ -137,123 +162,269 @@ enum ao2_callback_type {
  */
 #define EXTERNAL_OBJ(_p)	((_p) == NULL ? NULL : (_p)->user_data)
 
-/* the underlying functions common to debug and non-debug versions */
-
-static int internal_ao2_ref(void *user_data, const int delta);
-static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn,
-							  ao2_callback_fn *cmp_fn);
-static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func);
-static void *internal_ao2_callback(struct ao2_container *c,
-				   const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
-				   const char *tag, char *file, int line, const char *funcname);
-static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_entry **q);
-
-int __ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
+int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
 {
-	struct astobj2 *p = INTERNAL_OBJ(user_data);
+	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+	struct astobj2_lock *obj_mutex;
+	struct astobj2_rwlock *obj_rwlock;
+	int res = 0;
 
-	if (p == NULL)
+	if (obj == NULL) {
 		return -1;
+	}
 
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+		res = __ast_pthread_mutex_lock(file, line, func, var, &obj_mutex->mutex.lock);
+#ifdef AO2_DEBUG
+		if (!res) {
+			ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+		}
+#endif
+		break;
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+		switch (lock_how) {
+		case AO2_LOCK_REQ_MUTEX:
+		case AO2_LOCK_REQ_WRLOCK:
+			res = __ast_rwlock_wrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+			if (!res) {
+				ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
+#ifdef AO2_DEBUG
+				ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+			}
+			break;
+		case AO2_LOCK_REQ_RDLOCK:
+			res = __ast_rwlock_rdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+			if (!res) {
+				ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
 #ifdef AO2_DEBUG
-	ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+				ast_atomic_fetchadd_int(&ao2.total_locked, 1);
 #endif
+			}
+			break;
+		}
+		break;
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+		/* The ao2 object has no lock. */
+		break;
+	default:
+		ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+			user_data);
+		return -1;
+	}
 
-	return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
+	return res;
 }
 
 int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
 {
-	struct astobj2 *p = INTERNAL_OBJ(user_data);
+	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+	struct astobj2_lock *obj_mutex;
+	struct astobj2_rwlock *obj_rwlock;
+	int res = 0;
+	int current_value;
 
-	if (p == NULL)
+	if (obj == NULL) {
 		return -1;
+	}
 
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+		res = __ast_pthread_mutex_unlock(file, line, func, var, &obj_mutex->mutex.lock);
 #ifdef AO2_DEBUG
-	ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+		if (!res) {
+			ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+		}
 #endif
-
-	return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
+		break;
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+
+		current_value = ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1) - 1;
+		if (current_value < 0) {
+			/* It was a WRLOCK that we are unlocking.  Fix the count. */
+			ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -current_value);
+		}
+		res = __ast_rwlock_unlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+#ifdef AO2_DEBUG
+		if (!res) {
+			ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+		}
+#endif
+		break;
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+		/* The ao2 object has no lock. */
+		break;
+	default:
+		ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+			user_data);
+		res = -1;
+		break;
+	}
+	return res;
 }
 
-int __ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
+int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
 {
-	struct astobj2 *p = INTERNAL_OBJ(user_data);
-	int ret;
+	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+	struct astobj2_lock *obj_mutex;
+	struct astobj2_rwlock *obj_rwlock;
+	int res = 0;
 
-	if (p == NULL)
+	if (obj == NULL) {
 		return -1;
-	ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
+	}
 
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+		res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock);
 #ifdef AO2_DEBUG
-	if (!ret)
-		ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+		if (!res) {
+			ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+		}
 #endif
-	return ret;
-}
-
-void *ao2_object_get_lockaddr(void *obj)
-{
-	struct astobj2 *p = INTERNAL_OBJ(obj);
+		break;
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+		switch (lock_how) {
+		case AO2_LOCK_REQ_MUTEX:
+		case AO2_LOCK_REQ_WRLOCK:
+			res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+			if (!res) {
+				ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
+#ifdef AO2_DEBUG
+				ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+			}
+			break;
+		case AO2_LOCK_REQ_RDLOCK:
+			res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+			if (!res) {
+				ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
+#ifdef AO2_DEBUG
+				ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+			}
+			break;
+		}
+		break;
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+		/* The ao2 object has no lock. */
+		return 0;
+	default:
+		ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+			user_data);
+		return -1;
+	}
 
-	if (p == NULL)
-		return NULL;
 
-	return &p->priv_data.lock;
+	return res;
 }
 
-/*
- * The argument is a pointer to the user portion.
+/*!
+ * \internal
+ * \brief Adjust an object's lock to the requested level.
+ *
+ * \param user_data An ao2 object to adjust lock level.
+ * \param lock_how What level to adjust lock.
+ * \param keep_stronger TRUE if keep original lock level if it is stronger.
+ *
+ * \pre The ao2 object is already locked.
+ *
+ * \details
+ * An ao2 object with a RWLOCK will have its lock level adjusted
+ * to the specified level if it is not already there.  An ao2
+ * object with a different type of lock is not affected.
+ *
+ * \return Original lock level.
  */
-
-
-int __ao2_ref_debug(void *user_data, const int delta, const char *tag, char *file, int line, const char *funcname)
+static enum ao2_lock_req adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
 {
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
-
-	if (obj == NULL)
-		return -1;
-
-	if (delta != 0) {
-		FILE *refo = fopen(REF_FILE, "a");
-		if (refo) {
-			fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
-				delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
-			fclose(refo);
+	struct astobj2_rwlock *obj_rwlock;
+	enum ao2_lock_req orig_lock;
+
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+		if (obj_rwlock->rwlock.num_lockers < 0) {
+			orig_lock = AO2_LOCK_REQ_WRLOCK;
+		} else {
+			orig_lock = AO2_LOCK_REQ_RDLOCK;
 		}
-	}
-	if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
-		FILE *refo = fopen(REF_FILE, "a");
-		if (refo) {
-			fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
-			fclose(refo);
+		switch (lock_how) {
+		case AO2_LOCK_REQ_MUTEX:
+			lock_how = AO2_LOCK_REQ_WRLOCK;
+			/* Fall through */
+		case AO2_LOCK_REQ_WRLOCK:
+			if (lock_how != orig_lock) {
+				/* Switch from read lock to write lock. */
+				ao2_unlock(user_data);
+				ao2_wrlock(user_data);
+			}
+			break;
+		case AO2_LOCK_REQ_RDLOCK:
+			if (!keep_stronger && lock_how != orig_lock) {
+				/* Switch from write lock to read lock. */
+				ao2_unlock(user_data);
+				ao2_rdlock(user_data);
+			}
+			break;
 		}
+		break;
+	default:
+		ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
+		/* Fall through */
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		orig_lock = AO2_LOCK_REQ_MUTEX;
+		break;
 	}
-	return internal_ao2_ref(user_data, delta);
+
+	return orig_lock;
 }
 
-int __ao2_ref(void *user_data, const int delta)
+void *ao2_object_get_lockaddr(void *user_data)
 {
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+	struct astobj2_lock *obj_mutex;
 
-	if (obj == NULL)
-		return -1;
+	if (obj == NULL) {
+		return NULL;
+	}
+
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+		return &obj_mutex->mutex.lock;
+	default:
+		break;
+	}
 
-	return internal_ao2_ref(user_data, delta);
+	return NULL;
 }
 
-static int internal_ao2_ref(void *user_data, const int delta)
+static int internal_ao2_ref(void *user_data, int delta, const char *file, int line, const char *funcname)
 {
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+	struct astobj2_lock *obj_mutex;
+	struct astobj2_rwlock *obj_rwlock;
 	int current_value;
 	int ret;
 
-	if (obj == NULL)
+	if (obj == NULL) {
 		return -1;
+	}
 
 	/* if delta is 0, just return the refcount */
-	if (delta == 0)
-		return (obj->priv_data.ref_counter);
+	if (delta == 0) {
+		return obj->priv_data.ref_counter;
+	}
 
 	/* we modify with an atomic operation the reference counter */
 	ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
@@ -263,56 +434,169 @@ static int internal_ao2_ref(void *user_data, const int delta)
 	ast_atomic_fetchadd_int(&ao2.total_refs, delta);
 #endif
 
+	if (0 < current_value) {
+		/* The object still lives. */
+		return ret;
+	}
+
 	/* this case must never happen */
-	if (current_value < 0)
-		ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
+	if (current_value < 0) {
+		ast_log(__LOG_ERROR, file, line, funcname,
+			"Invalid refcount %d on ao2 object %p\n", current_value, user_data);
+	}
 
-	if (current_value <= 0) { /* last reference, destroy the object */
-		if (obj->priv_data.destructor_fn != NULL) {
-			obj->priv_data.destructor_fn(user_data);
-		}
+	/* last reference, destroy the object */
+	if (obj->priv_data.destructor_fn != NULL) {
+		obj->priv_data.destructor_fn(user_data);
+	}
 
-		ast_mutex_destroy(&obj->priv_data.lock);
 #ifdef AO2_DEBUG
-		ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
-		ast_atomic_fetchadd_int(&ao2.total_objects, -1);
+	ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
+	ast_atomic_fetchadd_int(&ao2.total_objects, -1);
 #endif
-		/* for safety, zero-out the astobj2 header and also the
+
+	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
+		obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+		ast_mutex_destroy(&obj_mutex->mutex.lock);
+
+		/*
+		 * For safety, zero-out the astobj2_lock header and also the
+		 * first word of the user-data, which we make sure is always
+		 * allocated.
+		 */
+		memset(obj_mutex, '\0', sizeof(*obj_mutex) + sizeof(void *) );
+		ast_free(obj_mutex);
+		break;
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+		ast_rwlock_destroy(&obj_rwlock->rwlock.lock);
+
+		/*
+		 * For safety, zero-out the astobj2_rwlock header and also the
 		 * first word of the user-data, which we make sure is always
-		 * allocated. */
-		memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
+		 * allocated.
+		 */
+		memset(obj_rwlock, '\0', sizeof(*obj_rwlock) + sizeof(void *) );
+		ast_free(obj_rwlock);
+		break;
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+		/*
+		 * For safety, zero-out the astobj2 header and also the first
+		 * word of the user-data, which we make sure is always
+		 * allocated.
+		 */
+		memset(obj, '\0', sizeof(*obj) + sizeof(void *) );
 		ast_free(obj);
+		break;
+	default:
+		ast_log(__LOG_ERROR, file, line, funcname,
+			"Invalid lock option on ao2 object %p\n", user_data);
+		break;
 	}
 
 	return ret;
 }
 
-/*
- * We always alloc at least the size of a void *,
- * for debugging purposes.
- */
-static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, const char *file, int line, const char *funcname)
+int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *funcname)
+{
+	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+	if (obj == NULL)
+		return -1;
+
+	if (delta != 0) {
+		FILE *refo = fopen(REF_FILE, "a");
+		if (refo) {
+			fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
+				delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
+			fclose(refo);
+		}
+	}
+	if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
+		FILE *refo = fopen(REF_FILE, "a");
+		if (refo) {
+			fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
+			fclose(refo);
+		}
+	}
+	return internal_ao2_ref(user_data, delta, file, line, funcname);
+}
+
+int __ao2_ref(void *user_data, int delta)
+{
+	struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+	if (obj == NULL)
+		return -1;
+
+	return internal_ao2_ref(user_data, delta, __FILE__, __LINE__, __FUNCTION__);
+}
+
+static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *file, int line, const char *funcname)
 {
 	/* allocation */
 	struct astobj2 *obj;
+	struct astobj2_lock *obj_mutex;
+	struct astobj2_rwlock *obj_rwlock;
 
-	if (data_size < sizeof(void *))
+	if (data_size < sizeof(void *)) {
+		/*
+		 * We always alloc at least the size of a void *,
+		 * for debugging purposes.
+		 */
 		data_size = sizeof(void *);
+	}
 
+	switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
+	case AO2_ALLOC_OPT_LOCK_MUTEX:
 #if defined(__AST_DEBUG_MALLOC)
-	obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
+		obj_mutex = __ast_calloc(1, sizeof(*obj_mutex) + data_size, file, line, funcname);
 #else
-	obj = ast_calloc(1, sizeof(*obj) + data_size);
+		obj_mutex = ast_calloc(1, sizeof(*obj_mutex) + data_size);
 #endif
+		if (obj_mutex == NULL) {
+			return NULL;
+		}
 
-	if (obj == NULL)
+		ast_mutex_init(&obj_mutex->mutex.lock);
+		obj = (struct astobj2 *) &obj_mutex->priv_data;
+		break;
+	case AO2_ALLOC_OPT_LOCK_RWLOCK:
+#if defined(__AST_DEBUG_MALLOC)
+		obj_rwlock = __ast_calloc(1, sizeof(*obj_rwlock) + data_size, file, line, funcname);
+#else
+		obj_rwlock = ast_calloc(1, sizeof(*obj_rwlock) + data_size);
+#endif
+		if (obj_rwlock == NULL) {
+			return NULL;
+		}
+
+		ast_rwlock_init(&obj_rwlock->rwlock.lock);
+		obj = (struct astobj2 *) &obj_rwlock->priv_data;
+		break;
+	case AO2_ALLOC_OPT_LOCK_NOLOCK:
+#if defined(__AST_DEBUG_MALLOC)
+		obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
+#else
+		obj = ast_calloc(1, sizeof(*obj) + data_size);
+#endif
+		if (obj == NULL) {
+			return NULL;
+		}
+		break;
+	default:
+		/* Invalid option value. */
+		ast_log(__LOG_DEBUG, file, line, funcname, "Invalid lock option requested\n");
 		return NULL;
+	}
 
-	ast_mutex_init(&obj->priv_data.lock);
-	obj->priv_data.magic = AO2_MAGIC;
-	obj->priv_data.data_size = data_size;
+	/* Initialize common ao2 values. */
 	obj->priv_data.ref_counter = 1;
 	obj->priv_data.destructor_fn = destructor_fn;	/* can be NULL */
+	obj->priv_data.data_size = data_size;
+	obj->priv_data.options = options;
+	obj->priv_data.magic = AO2_MAGIC;
 
 #ifdef AO2_DEBUG
 	ast_atomic_fetchadd_int(&ao2.total_objects, 1);
@@ -324,14 +608,14 @@ static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_f
 	return EXTERNAL_OBJ(obj);
 }
 
-void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const char *tag,
-			const char *file, int line, const char *funcname, int ref_debug)
+void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
+	const char *file, int line, const char *funcname, int ref_debug)
 {
 	/* allocation */
 	void *obj;
 	FILE *refo;
 
-	if ((obj = internal_ao2_alloc(data_size, destructor_fn, file, line, funcname)) == NULL) {
+	if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, funcname)) == NULL) {
 		return NULL;
 	}
 
@@ -344,9 +628,9 @@ void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const
 	return obj;
 }
 
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options)
 {
-	return internal_ao2_alloc(data_size, destructor_fn, __FILE__, __LINE__, __FUNCTION__);
+	return internal_ao2_alloc(data_size, destructor_fn, options, __FILE__, __LINE__, __FUNCTION__);
 }
 
 
@@ -356,6 +640,17 @@ static void container_destruct(void *c);
 /* internal callback to destroy a container. */
 static void container_destruct_debug(void *c);
 
+/*!
+ * A structure to create a linked list of entries,
+ * used within a bucket.
+ * XXX \todo this should be private to the container code
+ */
+struct bucket_entry {
+	AST_LIST_ENTRY(bucket_entry) entry;
+	int version;
+	struct astobj2 *astobj;/* pointer to internal data */
+};
+
 /* each bucket in the container is a tailq. */
 AST_LIST_HEAD_NOLOCK(bucket, bucket_entry);
 
@@ -410,14 +705,15 @@ static int hash_zero(const void *user_obj, const int flags)
 /*
  * A container is just an object, after all!
  */
-static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn,
-							  ao2_callback_fn *cmp_fn)
+static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c,
+	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
 {
 	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
 
-	if (!c)
+	if (!c) {
 		return NULL;
+	}
 
 	c->version = 1;	/* 0 is a reserved value here */
 	c->n_buckets = hash_fn ? n_buckets : 1;
@@ -431,28 +727,27 @@ static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *
 	return c;
 }
 
-struct ao2_container *__ao2_container_alloc_debug(unsigned int n_buckets, ao2_hash_fn *hash_fn,
-						  ao2_callback_fn *cmp_fn, const char *tag, char *file, int line,
-						  const char *funcname, int ref_debug)
+struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
+	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
+	const char *tag, const char *file, int line, const char *funcname, int ref_debug)
 {
 	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
-	const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+	unsigned int num_buckets = hash_fn ? n_buckets : 1;
 	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
-	struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname, ref_debug);
+	struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, options, tag, file, line, funcname, ref_debug);
 
 	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
 }
 
-struct ao2_container *__ao2_container_alloc(unsigned int n_buckets, ao2_hash_fn *hash_fn,
-					    ao2_callback_fn *cmp_fn)
+struct ao2_container *__ao2_container_alloc(unsigned int options,
+	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
 {
 	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
-
 	const unsigned int num_buckets = hash_fn ? n_buckets : 1;
 	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
-	struct ao2_container *c = __ao2_alloc(container_size, container_destruct);
+	struct ao2_container *c = __ao2_alloc(container_size, container_destruct, options);
 
 	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
 }
@@ -465,77 +760,68 @@ int ao2_container_count(struct ao2_container *c)
 	return c->elements;
 }
 
-/*!
- * A structure to create a linked list of entries,
- * used within a bucket.
- * XXX \todo this should be private to the container code
- */
-struct bucket_entry {
-	AST_LIST_ENTRY(bucket_entry) entry;
-	int version;
-	struct astobj2 *astobj;		/* pointer to internal data */
-};
-
 /*
  * link an object to a container
  */
-
-static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func)
+static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *funcname)
 {
 	int i;
+	enum ao2_lock_req orig_lock;
 	/* create a new list entry */
 	struct bucket_entry *p;
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
 
-	if (obj == NULL)
+	if (obj == NULL) {
 		return NULL;
+	}
 
-	if (INTERNAL_OBJ(c) == NULL)
+	if (INTERNAL_OBJ(c) == NULL) {
 		return NULL;
+	}
 
 	p = ast_calloc(1, sizeof(*p));
-	if (!p)
+	if (!p) {
 		return NULL;
+	}
 
 	i = abs(c->hash_fn(user_data, OBJ_POINTER));
 
-	if (!(flags & OBJ_NOLOCK)) {
-		ao2_lock(c);
+	if (flags & OBJ_NOLOCK) {
+		orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
+	} else {
+		ao2_wrlock(c);
+		orig_lock = AO2_LOCK_REQ_MUTEX;
 	}
+
 	i %= c->n_buckets;
 	p->astobj = obj;
 	p->version = ast_atomic_fetchadd_int(&c->version, 1);
 	AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
 	ast_atomic_fetchadd_int(&c->elements, 1);
 
-	/* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */
-	return p;
-}
-
-void *__ao2_link_debug(struct ao2_container *c, void *user_data, int flags, const char *tag, char *file, int line, const char *funcname)
-{
-	struct bucket_entry *p = internal_ao2_link(c, user_data, flags, file, line, funcname);
-
-	if (p) {
+	if (tag) {
 		__ao2_ref_debug(user_data, +1, tag, file, line, funcname);
-		if (!(flags & OBJ_NOLOCK)) {
-			ao2_unlock(c);
-		}
+	} else {
+		__ao2_ref(user_data, +1);
 	}
+
+	if (flags & OBJ_NOLOCK) {
+		adjust_lock(c, orig_lock, 0);
+	} else {
+		ao2_unlock(c);
+	}
+
 	return p;
 }
 
-void *__ao2_link(struct ao2_container *c, void *user_data, int flags)
+void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname)
 {
-	struct bucket_entry *p = internal_ao2_link(c, user_data, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__);
+	return internal_ao2_link(c, new_obj, flags, tag, file, line, funcname);
+}
 
-	if (p) {
-		__ao2_ref(user_data, +1);
-		if (!(flags & OBJ_NOLOCK)) {
-			ao2_unlock(c);
-		}
-	}
-	return p;
+void *__ao2_link(struct ao2_container *c, void *new_obj, int flags)
+{
+	return internal_ao2_link(c, new_obj, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
 }
 
 /*!
@@ -550,14 +836,14 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags)
  * Unlink an object from the container
  * and destroy the associated * bucket_entry structure.
  */
-void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags, const char *tag,
-			 char *file, int line, const char *funcname)
+void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
+	const char *tag, const char *file, int line, const char *funcname)
 {
-	if (INTERNAL_OBJ(user_data) == NULL)	/* safety check on the argument */
+	if (INTERNAL_OBJ(user_data) == NULL) {	/* safety check on the argument */
 		return NULL;
+	}
 
 	flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
-
 	__ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, funcname);
 
 	return NULL;
@@ -565,8 +851,9 @@ void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags, co
 
 void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
 {
-	if (INTERNAL_OBJ(user_data) == NULL)	/* safety check on the argument */
+	if (INTERNAL_OBJ(user_data) == NULL) {	/* safety check on the argument */
 		return NULL;
+	}
 
 	flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
 	__ao2_callback(c, flags, ao2_match_by_addr, user_data);
@@ -598,19 +885,21 @@ static int cb_true_data(void *user_data, void *arg, void *data, int flags)
  * aren't an excessive load to the system, as the callback should not be
  * called as often as, say, the ao2_ref func is called.
  */
-static void *internal_ao2_callback(struct ao2_container *c,
-				   const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
-				   const char *tag, char *file, int line, const char *funcname)
+static void *internal_ao2_callback(struct ao2_container *c, enum search_flags flags,
+	void *cb_fn, void *arg, void *data, enum ao2_callback_type type, const char *tag,
+	const char *file, int line, const char *funcname)
 {
 	int i, start, last;	/* search boundaries */
+	enum ao2_lock_req orig_lock;
 	void *ret = NULL;
 	ao2_callback_fn *cb_default = NULL;
 	ao2_callback_data_fn *cb_withdata = NULL;
 	struct ao2_container *multi_container = NULL;
 	struct ao2_iterator *multi_iterator = NULL;
 
-	if (INTERNAL_OBJ(c) == NULL)	/* safety check on the argument */
+	if (INTERNAL_OBJ(c) == NULL) {	/* safety check on the argument */
 		return NULL;
+	}
 
 	/*
 	 * This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
@@ -625,7 +914,8 @@ static void *internal_ao2_callback(struct ao2_container *c,
 		 * is destroyed, the container will be automatically
 		 * destroyed as well.
 		 */
-		if (!(multi_container = __ao2_container_alloc(1, NULL, NULL))) {
+		multi_container = __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+		if (!multi_container) {
 			return NULL;
 		}
 		if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
@@ -657,10 +947,13 @@ static void *internal_ao2_callback(struct ao2_container *c,
 	 * run the hash function. Otherwise, scan the whole container
 	 * (this only for the time being. We need to optimize this.)
 	 */
-	if ((flags & (OBJ_POINTER | OBJ_KEY)))	/* we know hash can handle this case */
+	if ((flags & (OBJ_POINTER | OBJ_KEY))) {
+		/* we know hash can handle this case */
 		start = i = c->hash_fn(arg, flags & (OBJ_POINTER | OBJ_KEY)) % c->n_buckets;
-	else			/* don't know, let's scan all buckets */
+	} else {
+		/* don't know, let's scan all buckets */
 		start = i = -1;		/* XXX this must be fixed later. */
+	}
 
 	/* determine the search boundaries: i..last-1 */
 	if (i < 0) {
@@ -672,9 +965,20 @@ static void *internal_ao2_callback(struct ao2_container *c,
 		last = i + 1;
 	}
 
-
-	if (!(flags & OBJ_NOLOCK)) {
-		ao2_lock(c);	/* avoid modifications to the content */
+	/* avoid modifications to the content */
+	if (flags & OBJ_NOLOCK) {
+		if (flags & OBJ_UNLINK) {
+			orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
+		} else {
+			orig_lock = adjust_lock(c, AO2_LOCK_REQ_RDLOCK, 1);
+		}
+	} else {
+		orig_lock = AO2_LOCK_REQ_MUTEX;
+		if (flags & OBJ_UNLINK) {
+			ao2_wrlock(c);
+		} else {
+			ao2_rdlock(c);
+		}
 	}
 
 	for (; i < last ; i++) {
@@ -710,7 +1014,7 @@ static void *internal_ao2_callback(struct ao2_container *c,
 				}
 			}
 
-			/* If we are in OBJ_MULTIPLE mode and OBJ_NODATE is off,
+			/* If we are in OBJ_MULTIPLE mode and OBJ_NODATA is off,
 			 * link the object into the container that will hold the results.
 			 */
 			if (ret && (multi_container != NULL)) {
@@ -764,14 +1068,16 @@ static void *internal_ao2_callback(struct ao2_container *c,
 		}
 	}
 
-	if (!(flags & OBJ_NOLOCK)) {
+	if (flags & OBJ_NOLOCK) {
+		adjust_lock(c, orig_lock, 0);
+	} else {
 		ao2_unlock(c);
 	}
 
 	/* if multi_container was created, we are returning multiple objects */
 	if (multi_container != NULL) {
 		*multi_iterator = ao2_iterator_init(multi_container,
-						    AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
+			AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
 		ao2_ref(multi_container, -1);
 		return multi_iterator;
 	} else {
@@ -780,7 +1086,7 @@ static void *internal_ao2_callback(struct ao2_container *c,
 }
 
 void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
-	ao2_callback_fn *cb_fn, void *arg, const char *tag, char *file, int line,
+	ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
 	const char *funcname)
 {
 	return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, funcname);
@@ -792,16 +1098,15 @@ void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
 	return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL);
 }
 
-void *__ao2_callback_data_debug(struct ao2_container *c,
-				enum search_flags flags,
-				ao2_callback_data_fn *cb_fn, void *arg, void *data,
-				const char *tag, char *file, int line, const char *funcname)
+void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
+	ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
+	int line, const char *funcname)
 {
 	return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, funcname);
 }
 
 void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
-			  ao2_callback_data_fn *cb_fn, void *arg, void *data)
+	ao2_callback_data_fn *cb_fn, void *arg, void *data)
 {
 	return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL);
 }
@@ -809,7 +1114,8 @@ void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
 /*!
  * the find function just invokes the default callback with some reasonable flags.
  */
-void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, char *file, int line, const char *funcname)
+void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
+	const char *tag, const char *file, int line, const char *funcname)
 {
 	void *arged = (void *) arg;/* Done to avoid compiler const warning */
 
@@ -854,26 +1160,39 @@ void ao2_iterator_destroy(struct ao2_iterator *i)
 /*
  * move to the next element in the container.
  */
-static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_entry **q)
+static void *internal_ao2_iterator_next(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname)
 {
 	int lim;
+	enum ao2_lock_req orig_lock;
 	struct bucket_entry *p = NULL;
-	void *ret = NULL;
-
-	*q = NULL;
+	void *ret;
 
-	if (INTERNAL_OBJ(a->c) == NULL)
+	if (INTERNAL_OBJ(a->c) == NULL) {
 		return NULL;
+	}
 
-	if (!(a->flags & AO2_ITERATOR_DONTLOCK))
-		ao2_lock(a->c);
+	if (a->flags & AO2_ITERATOR_DONTLOCK) {
+		if (a->flags & AO2_ITERATOR_UNLINK) {
+			orig_lock = adjust_lock(a->c, AO2_LOCK_REQ_WRLOCK, 1);
+		} else {
+			orig_lock = adjust_lock(a->c, AO2_LOCK_REQ_RDLOCK, 1);
+		}
+	} else {
+		orig_lock = AO2_LOCK_REQ_MUTEX;
+		if (a->flags & AO2_ITERATOR_UNLINK) {
+			ao2_wrlock(a->c);
+		} else {
+			ao2_rdlock(a->c);
+		}
+	}
 
 	/* optimization. If the container is unchanged and
 	 * we have a pointer, try follow it
 	 */
 	if (a->c->version == a->c_version && (p = a->obj)) {
-		if ((p = AST_LIST_NEXT(p, entry)))
+		if ((p = AST_LIST_NEXT(p, entry))) {
 			goto found;
+		}
 		/* nope, start from the next bucket */
 		a->bucket++;
 		a->version = 0;
@@ -891,8 +1210,9 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_en
 	for (; a->bucket < lim; a->bucket++, a->version = 0) {
 		/* scan the current bucket */
 		AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
-			if (p->version > a->version)
+			if (p->version > a->version) {
 				goto found;
+			}
 		}
 	}
 
@@ -913,48 +1233,35 @@ found:
 			a->version = p->version;
 			a->obj = p;
 			a->c_version = a->c->version;
+
 			/* inc refcount of returned object */
-			*q = p;
+			if (tag) {
+				__ao2_ref_debug(ret, 1, tag, file, line, funcname);
+			} else {
+				__ao2_ref(ret, 1);
+			}
 		}
+	} else {
+		ret = NULL;
+	}
+
+	if (a->flags & AO2_ITERATOR_DONTLOCK) {
+		adjust_lock(a->c, orig_lock, 0);
+	} else {
+		ao2_unlock(a->c);
 	}
 
 	return ret;
 }
 
-void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, char *file, int line, const char *funcname)
+void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname)
 {
-	struct bucket_entry *p;
-	void *ret = NULL;
-
-	ret = internal_ao2_iterator_next(a, &p);
-
-	if (p) {
-		/* inc refcount of returned object */
-		__ao2_ref_debug(ret, 1, tag, file, line, funcname);
-	}
-
-	if (!(a->flags & AO2_ITERATOR_DONTLOCK))
-		ao2_unlock(a->c);
-
-	return ret;
+	return internal_ao2_iterator_next(a, tag, file, line, funcname);
 }
 
 void *__ao2_iterator_next(struct ao2_iterator *a)
 {
-	struct bucket_entry *p = NULL;
-	void *ret = NULL;
-
-	ret = internal_ao2_iterator_next(a, &p);
-
-	if (p) {
-		/* inc refcount of returned object */
-		__ao2_ref(ret, 1);
-	}
-
-	if (!(a->flags & AO2_ITERATOR_DONTLOCK))
-		ao2_unlock(a->c);
-
-	return ret;
+	return internal_ao2_iterator_next(a, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
 }
 
 /* callback for destroying container.
@@ -1037,8 +1344,8 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
 	int res = 0;
 
 	if (!(flags & OBJ_NOLOCK)) {
-		ao2_lock(src);
-		ao2_lock(dest);
+		ao2_rdlock(src);
+		ao2_wrlock(dest);
 	}
 	obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
 	if (obj) {
@@ -1061,16 +1368,24 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
 {
 	struct ao2_container *clone;
+	struct astobj2 *orig_obj;
+	unsigned int options;
 	int failed;
 
+	orig_obj = INTERNAL_OBJ(orig);
+	if (!orig_obj) {
+		return NULL;
+	}
+	options = orig_obj->priv_data.options;
+
 	/* Create the clone container with the same properties as the original. */
-	clone = __ao2_container_alloc(orig->n_buckets, orig->hash_fn, orig->cmp_fn);
+	clone = __ao2_container_alloc(options, orig->n_buckets, orig->hash_fn, orig->cmp_fn);
 	if (!clone) {
 		return NULL;
 	}
 
 	if (flags & OBJ_NOLOCK) {
-		ao2_lock(clone);
+		ao2_wrlock(clone);
 	}
 	failed = ao2_container_dup(clone, orig, flags);
 	if (flags & OBJ_NOLOCK) {
@@ -1084,20 +1399,28 @@ struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum sea
 	return clone;
 }
 
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, char *file, int line, const char *funcname, int ref_debug)
+struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *funcname, int ref_debug)
 {
 	struct ao2_container *clone;
+	struct astobj2 *orig_obj;
+	unsigned int options;
 	int failed;
 
+	orig_obj = INTERNAL_OBJ(orig);
+	if (!orig_obj) {
+		return NULL;
+	}
+	options = orig_obj->priv_data.options;
+
 	/* Create the clone container with the same properties as the original. */
-	clone = __ao2_container_alloc_debug(orig->n_buckets, orig->hash_fn, orig->cmp_fn, tag,
-		file, line, funcname, ref_debug);
+	clone = __ao2_container_alloc_debug(options, orig->n_buckets, orig->hash_fn,
+		orig->cmp_fn, tag, file, line, funcname, ref_debug);
 	if (!clone) {
 		return NULL;
 	}
 
 	if (flags & OBJ_NOLOCK) {
-		ao2_lock(clone);
+		ao2_wrlock(clone);
 	}
 	failed = ao2_container_dup(clone, orig, flags);
 	if (flags & OBJ_NOLOCK) {
diff --git a/main/format.c b/main/format.c
index 82e3d2628561e4a39d1ad050142409a917540ebe..3af0f15f8bd426d65b1d272080c78db36b081d10 100644
--- a/main/format.c
+++ b/main/format.c
@@ -41,22 +41,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #define FORMAT_CONFIG "codecs.conf"
 
-/*! This is the container for all the format attribute interfaces.
- *  An ao2 container was chosen for fast lookup. */
+/*!
+ * \brief Container for all the format attribute interfaces.
+ * \note This container uses RWLOCKs instead of MUTEX locks.                                                             .
+ * \note An ao2 container was chosen for fast lookup.
+ */
 static struct ao2_container *interfaces;
 
-/*! This is the lock used to protect the interfaces container.  Yes, ao2_containers
- * do have their own locking, but we need the capability of performing read/write
- * locks on this specific container. */
-static ast_rwlock_t ilock;
-
 /*! a wrapper is used put interfaces into the ao2 container. */
 struct interface_ao2_wrapper {
 	enum ast_format_id id;
 	const struct ast_format_attr_interface *interface;
-	/*! a read write lock must be used to protect the wrapper instead
-	 * of the ao2 lock. */
-	ast_rwlock_t wraplock;
 };
 
 /*! \brief Format List container, This container is never directly accessed outside
@@ -84,12 +79,6 @@ static int interface_hash_cb(const void *obj, const int flags)
 	return wrapper->id;
 }
 
-static void interface_destroy_cb(void *obj)
-{
-	struct interface_ao2_wrapper *wrapper = obj;
-	ast_rwlock_destroy(&wrapper->wraplock);
-}
-
 void ast_format_copy(struct ast_format *dst, const struct ast_format *src)
 {
 	memcpy(dst, src, sizeof(struct ast_format));
@@ -107,16 +96,11 @@ int ast_format_get_video_mark(const struct ast_format *format)
 
 static struct interface_ao2_wrapper *find_interface(const struct ast_format *format)
 {
-	struct interface_ao2_wrapper *wrapper;
 	struct interface_ao2_wrapper tmp_wrapper = {
 		.id = format->id,
 	};
 
-	ast_rwlock_rdlock(&ilock);
-	wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK));
-	ast_rwlock_unlock(&ilock);
-
-	return wrapper;
+	return ao2_find(interfaces, &tmp_wrapper, OBJ_POINTER);
 }
 
 static int has_interface(const struct ast_format *format)
@@ -143,16 +127,16 @@ static int format_set_helper(struct ast_format *format, va_list ap)
 		return -1;
 	}
 
-	ast_rwlock_rdlock(&wrapper->wraplock);
+	ao2_rdlock(wrapper);
 	if (!wrapper->interface || !wrapper->interface->format_attr_set) {
-		ast_rwlock_unlock(&wrapper->wraplock);
+		ao2_unlock(wrapper);
 		ao2_ref(wrapper, -1);
 		return -1;
 	}
 
 	wrapper->interface->format_attr_set(&format->fattr, ap);
 
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 	ao2_ref(wrapper, -1);
 
 	return 0;
@@ -207,12 +191,12 @@ static int format_isset_helper(const struct ast_format *format, va_list ap)
 		return -1;
 	}
 
-	ast_rwlock_rdlock(&wrapper->wraplock);
+	ao2_rdlock(wrapper);
 	if (!wrapper->interface ||
 		!wrapper->interface->format_attr_set ||
 		!wrapper->interface->format_attr_cmp) {
 
-		ast_rwlock_unlock(&wrapper->wraplock);
+		ao2_unlock(wrapper);
 		ao2_ref(wrapper, -1);
 		return -1;
 	}
@@ -228,7 +212,7 @@ static int format_isset_helper(const struct ast_format *format, va_list ap)
 		res = (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0;
 	}
 
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 	ao2_ref(wrapper, -1);
 
 	return res;
@@ -249,21 +233,22 @@ int ast_format_get_value(const struct ast_format *format, int key, void *value)
 {
 	int res = 0;
 	struct interface_ao2_wrapper *wrapper;
+
 	if (!(wrapper = find_interface(format))) {
 		return -1;
 	}
-	ast_rwlock_rdlock(&wrapper->wraplock);
+	ao2_rdlock(wrapper);
 	if (!wrapper->interface ||
 		!wrapper->interface->format_attr_get_val) {
 
-		ast_rwlock_unlock(&wrapper->wraplock);
+		ao2_unlock(wrapper);
 		ao2_ref(wrapper, -1);
 		return -1;
 	}
 
 	res = wrapper->interface->format_attr_get_val(&format->fattr, key, value);
 
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 	ao2_ref(wrapper, -1);
 
 	return res;
@@ -281,16 +266,16 @@ static enum ast_format_cmp_res format_cmp_helper(const struct ast_format *format
 		return res;
 	}
 
-	ast_rwlock_rdlock(&wrapper->wraplock);
+	ao2_rdlock(wrapper);
 	if (!wrapper->interface || !wrapper->interface->format_attr_cmp) {
-		ast_rwlock_unlock(&wrapper->wraplock);
+		ao2_unlock(wrapper);
 		ao2_ref(wrapper, -1);
 		return res;
 	}
 
 	res = wrapper->interface->format_attr_cmp(&format1->fattr, &format2->fattr);
 
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 	ao2_ref(wrapper, -1);
 
 	return res;
@@ -318,11 +303,11 @@ static int format_joint_helper(const struct ast_format *format1, const struct as
 		return res;
 	}
 
-	ast_rwlock_rdlock(&wrapper->wraplock);
+	ao2_rdlock(wrapper);
 	if (wrapper->interface && wrapper->interface->format_attr_get_joint) {
 		res = wrapper->interface->format_attr_get_joint(&format1->fattr, &format2->fattr, &result->fattr);
 	}
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 
 	ao2_ref(wrapper, -1);
 
@@ -1066,21 +1051,13 @@ init_list_cleanup:
 int ast_format_attr_init(void)
 {
 	ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis));
-	if (ast_rwlock_init(&ilock)) {
-		return -1;
-	}
 
-	if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) {
-		goto init_cleanup;
+	interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
+		283, interface_hash_cb, interface_cmp_cb);
+	if (!interfaces) {
+		return -1;
 	}
 	return 0;
-
-init_cleanup:
-	ast_rwlock_destroy(&ilock);
-	if (interfaces) {
-		ao2_ref(interfaces, -1);
-	}
-	return -1;
 }
 
 static int custom_celt_format(struct ast_format_list *entry, unsigned int maxbitrate, unsigned int framesize)
@@ -1310,28 +1287,28 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf
 	 * anticipation of adding a new interface and to prevent a
 	 * duplicate from sneaking in between the check and add.
 	 */
-	ast_rwlock_wrlock(&ilock);
+	ao2_wrlock(interfaces);
 
 	/* check for duplicates first*/
 	if ((wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) {
-		ast_rwlock_unlock(&ilock);
+		ao2_unlock(interfaces);
 		ast_log(LOG_WARNING, "Can not register attribute interface for format id %d, interface already exists.\n", interface->id);
 		ao2_ref(wrapper, -1);
 		return -1;
 	}
 
-	if (!(wrapper = ao2_alloc(sizeof(*wrapper), interface_destroy_cb))) {
-		ast_rwlock_unlock(&ilock);
+	wrapper = ao2_alloc_options(sizeof(*wrapper), NULL, AO2_ALLOC_OPT_LOCK_RWLOCK);
+	if (!wrapper) {
+		ao2_unlock(interfaces);
 		return -1;
 	}
 
 	wrapper->interface = interface;
 	wrapper->id = interface->id;
-	ast_rwlock_init(&wrapper->wraplock);
 
 	/* The write lock is already held. */
-	ao2_link_nolock(interfaces, wrapper);
-	ast_rwlock_unlock(&ilock);
+	ao2_link_flags(interfaces, wrapper, OBJ_NOLOCK);
+	ao2_unlock(interfaces);
 
 	ao2_ref(wrapper, -1);
 
@@ -1359,17 +1336,13 @@ int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *inte
 		.id = interface->id,
 	};
 
-	/* use the write lock whenever the interface container is modified */
-	ast_rwlock_wrlock(&ilock);
-	if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK | OBJ_NOLOCK)))) {
-		ast_rwlock_unlock(&ilock);
+	if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK)))) {
 		return -1;
 	}
-	ast_rwlock_unlock(&ilock);
 
-	ast_rwlock_wrlock(&wrapper->wraplock);
+	ao2_wrlock(wrapper);
 	wrapper->interface = NULL;
-	ast_rwlock_unlock(&wrapper->wraplock);
+	ao2_unlock(wrapper);
 
 	ao2_ref(wrapper, -1);
 
diff --git a/main/format_cap.c b/main/format_cap.c
index bd471d4a7e8890cc446f7eca4475b4757d999f61..ffec45fe2b016afbb9add07babe64698bd41ca48 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -39,6 +39,7 @@ struct ast_format_cap {
 	/* The capabilities structure is just an ao2 container of ast_formats */
 	struct ao2_container *formats;
 	struct ao2_iterator it;
+	/*! TRUE if the formats container created without a lock. */
 	int nolock;
 };
 
@@ -69,8 +70,11 @@ static struct ast_format_cap *cap_alloc_helper(int nolock)
 	if (!cap) {
 		return NULL;
 	}
-	cap->nolock = nolock ? OBJ_NOLOCK : 0;
-	if (!(cap->formats = ao2_container_alloc(283, hash_cb, cmp_cb))) {
+	cap->nolock = nolock;
+	cap->formats = ao2_container_alloc_options(
+		nolock ? AO2_ALLOC_OPT_LOCK_NOLOCK : AO2_ALLOC_OPT_LOCK_MUTEX,
+		283, hash_cb, cmp_cb);
+	if (!cap->formats) {
 		ast_free(cap);
 		return NULL;
 	}
@@ -109,11 +113,7 @@ void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *for
 		return;
 	}
 	ast_format_copy(fnew, format);
-	if (cap->nolock) {
-		ao2_link_nolock(cap->formats, fnew);
-	} else {
-		ao2_link(cap->formats, fnew);
-	}
+	ao2_link(cap->formats, fnew);
 	ao2_ref(fnew, -1);
 }
 
@@ -157,7 +157,7 @@ static int append_cb(void *obj, void *arg, int flag)
 
 void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src)
 {
-	ao2_callback(src->formats, OBJ_NODATA | src->nolock, append_cb, dst);
+	ao2_callback(src->formats, OBJ_NODATA, append_cb, dst);
 }
 
 static int copy_cb(void *obj, void *arg, int flag)
@@ -172,7 +172,7 @@ static int copy_cb(void *obj, void *arg, int flag)
 void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src)
 {
 	ast_format_cap_remove_all(dst);
-	ao2_callback(src->formats, OBJ_NODATA | src->nolock, copy_cb, dst);
+	ao2_callback(src->formats, OBJ_NODATA, copy_cb, dst);
 }
 
 struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
@@ -186,7 +186,7 @@ struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
 	if (!dst) {
 		return NULL;
 	}
-	ao2_callback(cap->formats, OBJ_NODATA | cap->nolock, copy_cb, dst);
+	ao2_callback(cap->formats, OBJ_NODATA, copy_cb, dst);
 	return dst;
 }
 
@@ -209,8 +209,8 @@ static int find_exact_cb(void *obj, void *arg, int flag)
 int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
 {
 	struct ast_format *fremove;
-	fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK | cap->nolock, find_exact_cb, format);
 
+	fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK, find_exact_cb, format);
 	if (fremove) {
 		ao2_ref(fremove, -1);
 		return 0;
@@ -249,7 +249,7 @@ int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id
 	};
 
 	ao2_callback(cap->formats,
-		OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK,
+		OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
 		multiple_by_id_cb,
 		&data);
 
@@ -271,14 +271,14 @@ static int multiple_by_type_cb(void *obj, void *arg, int flag)
 void ast_format_cap_remove_bytype(struct ast_format_cap *cap, enum ast_format_type type)
 {
 	ao2_callback(cap->formats,
-		OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | cap->nolock,
+		OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
 		multiple_by_type_cb,
 		&type);
 }
 
 void ast_format_cap_remove_all(struct ast_format_cap *cap)
 {
-	ao2_callback(cap->formats, OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
+	ao2_callback(cap->formats, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
 }
 
 void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format)
@@ -291,8 +291,8 @@ int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const
 {
 	struct ast_format *f;
 	struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
-	f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
 
+	f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
 	if (f) {
 		ast_format_copy(result, f);
 		ao2_ref(f, -1);
@@ -310,7 +310,8 @@ int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct a
 	if (!tmp_cap) {
 		return 0;
 	}
-	f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
+
+	f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
 	if (f) {
 		ao2_ref(f, -1);
 		return 1;
@@ -345,7 +346,7 @@ int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_i
 
 	ast_format_clear(result);
 	ao2_callback(cap->formats,
-		OBJ_MULTIPLE | OBJ_NODATA | cap->nolock,
+		OBJ_MULTIPLE | OBJ_NODATA,
 		find_best_byid_cb,
 		&data);
 	return result->id ? 1 : 0;
@@ -389,11 +390,11 @@ int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast
 		.joint_cap = NULL,
 	};
 
-	it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap1->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		data.format = tmp;
 		ao2_callback(cap2->formats,
-			OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+			OBJ_MULTIPLE | OBJ_NODATA,
 			find_joint_cb,
 			&data);
 		ao2_ref(tmp, -1);
@@ -412,7 +413,7 @@ int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast
 		return 0; /* if they are not the same size, they are not identical */
 	}
 
-	it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap1->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		if (!ast_format_cap_iscompatible(cap2, tmp)) {
 			ao2_ref(tmp, -1);
@@ -439,11 +440,11 @@ struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, c
 		return NULL;
 	}
 
-	it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap1->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		data.format = tmp;
 		ao2_callback(cap2->formats,
-			OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+			OBJ_MULTIPLE | OBJ_NODATA,
 			find_joint_cb,
 			&data);
 		ao2_ref(tmp, -1);
@@ -469,11 +470,11 @@ static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast
 	if (!append) {
 		ast_format_cap_remove_all(result);
 	}
-	it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap1->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		data.format = tmp;
 		ao2_callback(cap2->formats,
-			OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+			OBJ_MULTIPLE | OBJ_NODATA,
 			find_joint_cb,
 			&data);
 		ao2_ref(tmp, -1);
@@ -505,7 +506,7 @@ struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap,
 
 	/* for each format in cap1, see if that format is
 	 * compatible with cap2. If so copy it to the result */
-	it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) {
 			/* copy format */
@@ -529,7 +530,7 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_ty
 	struct ao2_iterator it;
 	struct ast_format *tmp;
 
-	it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		if (AST_FORMAT_GET_TYPE(tmp->id) == type) {
 			ao2_ref(tmp, -1);
@@ -545,18 +546,16 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_ty
 
 void ast_format_cap_iter_start(struct ast_format_cap *cap)
 {
-	if (!cap->nolock) {
-		ao2_lock(cap->formats);
-	}
-	cap->it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	/* We can unconditionally lock even if the container has no lock. */
+	ao2_lock(cap->formats);
+	cap->it = ao2_iterator_init(cap->formats, AO2_ITERATOR_DONTLOCK);
 }
 
 void ast_format_cap_iter_end(struct ast_format_cap *cap)
 {
 	ao2_iterator_destroy(&cap->it);
-	if (!cap->nolock) {
-		ao2_unlock(cap->formats);
-	}
+	/* We can unconditionally unlock even if the container has no lock. */
+	ao2_unlock(cap->formats);
 }
 
 int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format)
@@ -614,7 +613,7 @@ uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap)
 	struct ao2_iterator it;
 	struct ast_format *tmp;
 
-	it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+	it = ao2_iterator_init(cap->formats, 0);
 	while ((tmp = ao2_iterator_next(&it))) {
 		res |= ast_format_to_old_bitfield(tmp);
 		ao2_ref(tmp, -1);