<p>Hoernchen has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-mgw/+/25432">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add multithreading for the virtual trunk<br><br>The idea is rather easy: do not disturb the code too much, so each<br>thread operates on a mgcp_trunk sub-trunk that has<br>- (it is) a mgcp_trunk structure just like the parent trunk<br>- some thread-specific information in struct per_thread_info<br>* this exists in the sub-trunk, as a SINGLE pointer (this_thread_info)<br>to the threads own info<br>- a different endpoint begin offset<br>- and different number of endpoint<br>- a private copy (!) of the mgcp_config structure that allows selective<br>updates of config settings during runtime using the vty<br><br>The thread-trunks doen't really know that they are not a full trunk, as<br>far as actual "mgw-operation" is concerned<br><br>Most of the other changes deal with the (t)alloc contexts to ensure<br>proper parent contexts that are not mixed between threads, the only<br>talloc that is threadsafe is the null context with disabled null<br>tracking, which is plain old malloc.<br><br>A parent trunk is configured using the vty/config as usual, so it also<br>has all the endpoints, but those are just not being used by anything,<br>all of the structs just exist to allow parsing and configuring, but the<br>updates are then also sent to the trunk threads. It contains the<br>thread_info array which has one per_thread_info struct entry for every<br>trunkthread of this trunk.<br><br>Communication betwen the main threads and the trunk thread(s) work by<br>sending messages through<br>- the mgcp msg queue for mgcp commands, which the thread then ansers by<br>writing to the socket, the queue back to the main thread is currently<br>unused.<br>- the cfg/vty command queue for vty commands and vty settings, that are<br>parsed and filtered by the threads own functions, the return queue to<br>the main thread is only being used to block it during vty show commands<br>that print in threads.<br><br>MGCP message handling is therefore split between a quick parsing in the<br>main thread to determine which endpoint (-> thread) should handle the<br>command and wrapping the command in a interthread queue buffer before<br>submitting it to one (or multiple) threads, and the usual processing<br>within the sub-trunk threads.<br><br>Multithreading is by default disabled unless "number threads" in the<br>config file exists.<br><br>Multithreading is disabled for e1 trunks due to the complexity of the<br>code and a lack of test coverage that reliably proves the absence of<br>threading related issues, so e1 is still being handled by the main<br>thread. Multithreading is also disabled for all trunks if osmux is<br>configured, also due to complexity and the fact that one osmux trunk is<br>limited to 256 calls due to the CID anyway, which can be handled within<br>one (main) thread.<br><br>That being said the code is ready to enable threads for all of that,<br>it's just not possible to prove that it will not implode at some point..<br><br>Change-Id: I31be8253600c8af0a43c967d0d128f5ba7b16260<br>---<br>M include/osmocom/mgcp/Makefile.am<br>M include/osmocom/mgcp/mgcp.h<br>M include/osmocom/mgcp/mgcp_endp.h<br>A include/osmocom/mgcp/mgcp_threads.h<br>M include/osmocom/mgcp/mgcp_trunk.h<br>M include/osmocom/mgcp/osmux.h<br>M src/libosmo-mgcp/Makefile.am<br>M src/libosmo-mgcp/mgcp_endp.c<br>M src/libosmo-mgcp/mgcp_osmux.c<br>M src/libosmo-mgcp/mgcp_protocol.c<br>M src/libosmo-mgcp/mgcp_sdp.c<br>M src/libosmo-mgcp/mgcp_stat.c<br>A src/libosmo-mgcp/mgcp_threads.c<br>A src/libosmo-mgcp/mgcp_threads_vty.c<br>M src/libosmo-mgcp/mgcp_trunk.c<br>M src/libosmo-mgcp/mgcp_vty.c<br>M src/osmo-mgw/mgw_main.c<br>M tests/mgcp/mgcp_test.c<br>18 files changed, 1,311 insertions(+), 437 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/32/25432/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am</span><br><span>index b94cdcd..9004cc8 100644</span><br><span>--- a/include/osmocom/mgcp/Makefile.am</span><br><span>+++ b/include/osmocom/mgcp/Makefile.am</span><br><span>@@ -13,4 +13,6 @@</span><br><span>         mgcp_e1.h \</span><br><span>  mgcp_network.h \</span><br><span>     mgcp_protocol.h \</span><br><span style="color: hsl(120, 100%, 40%);">+     mgcp_threads.h \</span><br><span style="color: hsl(120, 100%, 40%);">+      mgcp_threads_queue.h \</span><br><span>       $(NULL)</span><br><span>diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h</span><br><span>index f1e6460..fffff96 100644</span><br><span>--- a/include/osmocom/mgcp/mgcp.h</span><br><span>+++ b/include/osmocom/mgcp/mgcp.h</span><br><span>@@ -151,29 +151,33 @@</span><br><span>     mgcp_rqnt rqnt_cb;</span><br><span>   void *data;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       unsigned int num_threads_for_virttrunk;</span><br><span>      /* list holding the trunks */</span><br><span>        struct llist_head trunks;</span><br><span> </span><br><span>        enum mgcp_role role;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        /* osmux translator: 0 means disabled, 1 means enabled */</span><br><span style="color: hsl(0, 100%, 40%);">-       int osmux;</span><br><span style="color: hsl(0, 100%, 40%);">-      /* addr to bind the server to */</span><br><span style="color: hsl(0, 100%, 40%);">-        char osmux_addr[INET6_ADDRSTRLEN];</span><br><span style="color: hsl(0, 100%, 40%);">-      /* The BSC-NAT may ask for enabling osmux on demand. This tells us if</span><br><span style="color: hsl(0, 100%, 40%);">-    * the osmux socket is already initialized.</span><br><span style="color: hsl(0, 100%, 40%);">-      */</span><br><span style="color: hsl(0, 100%, 40%);">-     int osmux_init;</span><br><span style="color: hsl(0, 100%, 40%);">- /* osmux batch factor: from 1 to 4 maximum */</span><br><span style="color: hsl(0, 100%, 40%);">-   int osmux_batch;</span><br><span style="color: hsl(0, 100%, 40%);">-        /* osmux batch size (in bytes) */</span><br><span style="color: hsl(0, 100%, 40%);">-       int osmux_batch_size;</span><br><span style="color: hsl(0, 100%, 40%);">-   /* osmux port */</span><br><span style="color: hsl(0, 100%, 40%);">-        uint16_t osmux_port;</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Pad circuit with dummy messages until we see the first voice</span><br><span style="color: hsl(0, 100%, 40%);">-  * message.</span><br><span style="color: hsl(0, 100%, 40%);">-      */</span><br><span style="color: hsl(0, 100%, 40%);">-     uint16_t osmux_dummy;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct global_osmux_options_t {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* osmux translator: 0 means disabled, 1 means enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+             int osmux;</span><br><span style="color: hsl(120, 100%, 40%);">+            /* addr to bind the server to */</span><br><span style="color: hsl(120, 100%, 40%);">+              char osmux_addr[INET6_ADDRSTRLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+            /* The BSC-NAT may ask for enabling osmux on demand. This tells us if</span><br><span style="color: hsl(120, 100%, 40%);">+         * the osmux socket is already initialized.</span><br><span style="color: hsl(120, 100%, 40%);">+            */</span><br><span style="color: hsl(120, 100%, 40%);">+            int osmux_init;</span><br><span style="color: hsl(120, 100%, 40%);">+               /* osmux batch factor: from 1 to 4 maximum */</span><br><span style="color: hsl(120, 100%, 40%);">+         int osmux_batch;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* osmux batch size (in bytes) */</span><br><span style="color: hsl(120, 100%, 40%);">+             int osmux_batch_size;</span><br><span style="color: hsl(120, 100%, 40%);">+         /* osmux port */</span><br><span style="color: hsl(120, 100%, 40%);">+              uint16_t osmux_port;</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Pad circuit with dummy messages until we see the first voice</span><br><span style="color: hsl(120, 100%, 40%);">+               * message.</span><br><span style="color: hsl(120, 100%, 40%);">+            */</span><br><span style="color: hsl(120, 100%, 40%);">+            uint16_t osmux_dummy;</span><br><span style="color: hsl(120, 100%, 40%);">+ } global_osmux_options;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    /* domain name of the media gateway */</span><br><span>       char domain[255+1];</span><br><span> </span><br><span>@@ -198,7 +202,11 @@</span><br><span> /*</span><br><span>  * format helper functions</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+struct to_trunkthread_mgcp_msg;</span><br><span style="color: hsl(120, 100%, 40%);">+struct per_thread_info;</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct to_trunkthread_mgcp_msg* w);</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *mgcp_submit_message_to_trunkthread(struct mgcp_config *cfg, struct to_trunkthread_mgcp_msg* w);</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb* thread_handle_mgcp_message(struct to_trunkthread_mgcp_msg* w, struct per_thread_info *thread_info);</span><br><span> </span><br><span> </span><br><span> int mgcp_send_reset_ep(struct mgcp_endpoint *endp);</span><br><span>diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h</span><br><span>index 91c4551..47592ae 100644</span><br><span>--- a/include/osmocom/mgcp/mgcp_endp.h</span><br><span>+++ b/include/osmocom/mgcp/mgcp_endp.h</span><br><span>@@ -136,6 +136,7 @@</span><br><span> bool mgcp_endp_avail(struct mgcp_endpoint *endp);</span><br><span> void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);</span><br><span> void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);</span><br><span style="color: hsl(120, 100%, 40%);">+void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk);</span><br><span> void mgcp_endp_strip_name(char *epname_stripped, const char *epname,</span><br><span>                      const struct mgcp_trunk *trunk);</span><br><span> struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,</span><br><span>diff --git a/include/osmocom/mgcp/mgcp_threads.h b/include/osmocom/mgcp/mgcp_threads.h</span><br><span>new file mode 100644</span><br><span>index 0000000..2ee44d7</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/mgcp/mgcp_threads.h</span><br><span>@@ -0,0 +1,127 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Eric Wild</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <assert.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <pthread.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdatomic.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_protocol.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_msg.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_trunk.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads_queue.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void split_trunks_into_threads(struct mgcp_config *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+int get_trunk_thread_for_ep_name(const char *epname, struct mgcp_trunk *thread_parent_trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+void send_async_vty_trunk_update_msg(struct mgcp_trunk *t);</span><br><span style="color: hsl(120, 100%, 40%);">+void send_async_vty_cfg_update_msg(struct mgcp_config *c);</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_dispatch_ep_dump_msg(struct mgcp_trunk *trunk, int threadnum, struct vty *vty, const char *epname,</span><br><span style="color: hsl(120, 100%, 40%);">+                                bool show_stats, bool active_only);</span><br><span style="color: hsl(120, 100%, 40%);">+enum trunkthread_cfg_msg_t;</span><br><span style="color: hsl(120, 100%, 40%);">+struct to_trunkthread_cfg_msg;</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_dispatch_ep_msg(struct mgcp_trunk *master_trunk, int threadnum, struct vty *v, enum trunkthread_cfg_msg_t,</span><br><span style="color: hsl(120, 100%, 40%);">+                         struct to_trunkthread_cfg_msg *m);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Request data passed to the request handler */</span><br><span style="color: hsl(120, 100%, 40%);">+struct mgcp_request_data {</span><br><span style="color: hsl(120, 100%, 40%);">+        /* request name (e.g. "MDCX") */</span><br><span style="color: hsl(120, 100%, 40%);">+    char name[4 + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* parsing results from the MGCP header (trans id, endpoint name ...) */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct mgcp_parse_data *pdatap;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* pointer to endpoint resource (may be NULL for wildcarded requests) */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct mgcp_endpoint *endp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* pointer to trunk resource */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct mgcp_trunk *trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* set to true when the request has been classified as wildcarded */</span><br><span style="color: hsl(120, 100%, 40%);">+  bool wildcarded;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* contains cause code in case of problems during endp/trunk resolution */</span><br><span style="color: hsl(120, 100%, 40%);">+    int mgcp_cause;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum cfg_content_t { HAS_TRUNK = 1 << 0, HAS_CFG = 1 << 1 };</span><br><span style="color: hsl(120, 100%, 40%);">+enum trunkthread_cfg_msg_t {</span><br><span style="color: hsl(120, 100%, 40%);">+ IS_INVALID = 0 << 0,</span><br><span style="color: hsl(120, 100%, 40%);">+    IS_CFGMSG = 1 << 0,</span><br><span style="color: hsl(120, 100%, 40%);">+     IS_VTYMSG = 1 << 1,</span><br><span style="color: hsl(120, 100%, 40%);">+     IS_FREEEPMSG = 1 << 2,</span><br><span style="color: hsl(120, 100%, 40%);">+  IS_LOOPMSG = 1 << 3,</span><br><span style="color: hsl(120, 100%, 40%);">+    IS_TAPMSG = 1 << 4</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+struct to_trunkthread_cfg_msg {</span><br><span style="color: hsl(120, 100%, 40%);">+ enum trunkthread_cfg_msg_t type;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct vty *vty;</span><br><span style="color: hsl(120, 100%, 40%);">+      union {</span><br><span style="color: hsl(120, 100%, 40%);">+               struct cfgmsg {</span><br><span style="color: hsl(120, 100%, 40%);">+                       enum cfg_content_t content;</span><br><span style="color: hsl(120, 100%, 40%);">+                   struct mgcp_config c;</span><br><span style="color: hsl(120, 100%, 40%);">+                 struct mgcp_trunk t;</span><br><span style="color: hsl(120, 100%, 40%);">+          } c;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct vtymsg {</span><br><span style="color: hsl(120, 100%, 40%);">+                       char epname[MGCP_ENDPOINT_MAXLEN]; /* may be empty */</span><br><span style="color: hsl(120, 100%, 40%);">+                 bool show_stats;</span><br><span style="color: hsl(120, 100%, 40%);">+                      bool active_only;</span><br><span style="color: hsl(120, 100%, 40%);">+             } v;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct freepmsg {</span><br><span style="color: hsl(120, 100%, 40%);">+                     char endp[MGCP_ENDPOINT_MAXLEN * 4];</span><br><span style="color: hsl(120, 100%, 40%);">+          } f;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct tapmsg {</span><br><span style="color: hsl(120, 100%, 40%);">+                       char epname[MGCP_ENDPOINT_MAXLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+                    char connid[MGCP_CONN_ID_MAXLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+                     bool direction_is_in;</span><br><span style="color: hsl(120, 100%, 40%);">+                 char addr[INET6_ADDRSTRLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+                  unsigned short destport;</span><br><span style="color: hsl(120, 100%, 40%);">+              } t;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct loopmsg {</span><br><span style="color: hsl(120, 100%, 40%);">+                      char epname[MGCP_ENDPOINT_MAXLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+                    bool enable_loop;</span><br><span style="color: hsl(120, 100%, 40%);">+             } l;</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+struct to_trunkthread_mgcp_msg {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct y {</span><br><span style="color: hsl(120, 100%, 40%);">+            ssize_t msglen;</span><br><span style="color: hsl(120, 100%, 40%);">+               bool successfully_parsed;</span><br><span style="color: hsl(120, 100%, 40%);">+             struct osmo_sockaddr addr;</span><br><span style="color: hsl(120, 100%, 40%);">+            struct mgcp_parse_data pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+         struct mgcp_request_data rq;</span><br><span style="color: hsl(120, 100%, 40%);">+  } x;</span><br><span style="color: hsl(120, 100%, 40%);">+  char msg[4096 - sizeof(struct y)];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+static_assert(sizeof(struct to_trunkthread_mgcp_msg) == 4096, "interthread struct size mismatch!");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct per_thread_info {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct mgcp_trunk *parent_trunk; /* pointer to parent trunk */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct mgcp_trunk *this_trunk; /* talloced, used as ctx */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct mgcp_config *this_cfg; /* talloced, used as ctx */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct qchan chan_mgcp;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct qchan chan_cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+        int tid; /* thread number handling this subtrunk */</span><br><span style="color: hsl(120, 100%, 40%);">+   pthread_t thr;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fd master_evfd; /* registered by dispatch thread */</span><br><span style="color: hsl(120, 100%, 40%);">+       atomic_uint eps_free;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_push_msg(struct mgcp_trunk *trunk, unsigned int threadnum, void *elem);</span><br><span>diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h</span><br><span>index 3f14f97..db98ca7 100644</span><br><span>--- a/include/osmocom/mgcp/mgcp_trunk.h</span><br><span>+++ b/include/osmocom/mgcp/mgcp_trunk.h</span><br><span>@@ -22,6 +22,16 @@</span><br><span> struct mgcp_trunk {</span><br><span>        struct llist_head entry;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  /*      master trunk struct: array of num_threads trunk info structs containing per thread data</span><br><span style="color: hsl(120, 100%, 40%);">+               slave thread trunk struct: single entry pointing to this threads info!</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+    union {</span><br><span style="color: hsl(120, 100%, 40%);">+               struct per_thread_info *thread_info;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct per_thread_info *this_thread_info;</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+    int num_threads;</span><br><span style="color: hsl(120, 100%, 40%);">+      bool use_threads; /* false for e1 */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       struct mgcp_config *cfg;</span><br><span> </span><br><span>         unsigned int trunk_nr;</span><br><span>@@ -52,6 +62,7 @@</span><br><span>   int rtp_accept_all;</span><br><span> </span><br><span>      unsigned int number_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+        unsigned int number_endpoints_offset;</span><br><span>        struct mgcp_endpoint **endpoints;</span><br><span> </span><br><span>        /* rate counters and stat items to measure the trunks overall performance and health */</span><br><span>diff --git a/include/osmocom/mgcp/osmux.h b/include/osmocom/mgcp/osmux.h</span><br><span>index 99b44d1..2230a71 100644</span><br><span>--- a/include/osmocom/mgcp/osmux.h</span><br><span>+++ b/include/osmocom/mgcp/osmux.h</span><br><span>@@ -12,6 +12,10 @@</span><br><span>    OSMUX_ROLE_BSC_NAT,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct global_osmux_options_t;</span><br><span style="color: hsl(120, 100%, 40%);">+void osmux_set_global_opt(struct global_osmux_options_t *ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+struct global_osmux_options_t * osmux_get_global_opt();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int osmux_init(int role, struct mgcp_config *cfg);</span><br><span> int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,</span><br><span>                    struct osmo_sockaddr *addr, uint16_t port);</span><br><span>diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am</span><br><span>index 91b2bf6..e791a94 100644</span><br><span>--- a/src/libosmo-mgcp/Makefile.am</span><br><span>+++ b/src/libosmo-mgcp/Makefile.am</span><br><span>@@ -48,4 +48,7 @@</span><br><span>   mgcp_ctrl.c \</span><br><span>        mgcp_ratectr.c \</span><br><span>     mgcp_e1.c \</span><br><span style="color: hsl(120, 100%, 40%);">+   mgcp_threads.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      mgcp_threads_queue.c \</span><br><span style="color: hsl(120, 100%, 40%);">+        mgcp_threads_vty.c \</span><br><span>         $(NULL)</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c</span><br><span>index 9846dfe..49e7624 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_endp.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_endp.c</span><br><span>@@ -26,6 +26,7 @@</span><br><span> #include <osmocom/mgcp/mgcp_conn.h></span><br><span> #include <osmocom/mgcp/mgcp_endp.h></span><br><span> #include <osmocom/mgcp/mgcp_trunk.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span> </span><br><span> #include <osmocom/abis/e1_input.h></span><br><span> #include <osmocom/mgcp/mgcp_e1.h></span><br><span>@@ -124,9 +125,11 @@</span><br><span> </span><br><span>         /* We must only decrement the stat item when the endpoint as actually</span><br><span>         * claimed. An endpoint is claimed when a call-id is set */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (endp->callid)</span><br><span style="color: hsl(120, 100%, 40%);">+  if (endp->callid) {</span><br><span>               osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,</span><br><span>                                                             TRUNK_STAT_ENDPOINTS_USED), 1);</span><br><span style="color: hsl(120, 100%, 40%);">+              endp->trunk->this_thread_info->eps_free++;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span> </span><br><span>        /* Reset endpoint parameters and states */</span><br><span>   talloc_free(endp->callid);</span><br><span>@@ -144,7 +147,7 @@</span><br><span>  * "ds/e1-") and write the epname without the prefix back to the memory</span><br><span>  * pointed at by epname. (per trunk the prefix is the same for all endpoints,</span><br><span>  * so no ambiguity is introduced) */</span><br><span style="color: hsl(0, 100%, 40%);">-static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)</span><br><span style="color: hsl(120, 100%, 40%);">+void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)</span><br><span> {</span><br><span>    size_t prefix_len;</span><br><span>   switch (trunk->trunk_type) {</span><br><span>@@ -606,6 +609,7 @@</span><br><span>        OSMO_ASSERT(endp->callid);</span><br><span>        osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common,</span><br><span>                                                     TRUNK_STAT_ENDPOINTS_USED), 1);</span><br><span style="color: hsl(120, 100%, 40%);">+      endp->trunk->this_thread_info->eps_free--;</span><br><span> </span><br><span>      /* Allocate resources */</span><br><span>     switch (endp->trunk->trunk_type) {</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c</span><br><span>index de19042..03aa71a 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_osmux.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_osmux.c</span><br><span>@@ -44,6 +44,16 @@</span><br><span> </span><br><span> static void *osmux;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct global_osmux_options_t *g_osmux;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmux_set_global_opt(struct global_osmux_options_t *ptr) {</span><br><span style="color: hsl(120, 100%, 40%);">+      g_osmux = ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct global_osmux_options_t * osmux_get_global_opt() {</span><br><span style="color: hsl(120, 100%, 40%);">+ return g_osmux;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Deliver OSMUX batch to the remote end */</span><br><span> static void osmux_deliver_cb(struct msgb *batch_msg, void *data)</span><br><span> {</span><br><span>@@ -135,10 +145,10 @@</span><br><span>         /* sequence number to start OSMUX message from */</span><br><span>    h->in->osmux_seq = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- h->in->batch_factor = cfg->osmux_batch;</span><br><span style="color: hsl(120, 100%, 40%);">+      h->in->batch_factor = g_osmux->osmux_batch;</span><br><span> </span><br><span>     /* If batch size is zero, the library defaults to 1470 bytes. */</span><br><span style="color: hsl(0, 100%, 40%);">-        h->in->batch_size = cfg->osmux_batch_size;</span><br><span style="color: hsl(120, 100%, 40%);">+   h->in->batch_size = g_osmux->osmux_batch_size;</span><br><span>      h->in->deliver = osmux_deliver_cb;</span><br><span>     osmux_xfrm_input_init(h->in);</span><br><span>     h->in->data = h;</span><br><span>@@ -368,7 +378,7 @@</span><br><span>         if (!msg)</span><br><span>            return -1;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (!cfg->osmux) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!g_osmux->osmux) {</span><br><span>            LOGP(DLMGCP, LOGL_ERROR,</span><br><span>                  "bsc-nat wants to use Osmux but bsc did not request it\n");</span><br><span>           goto out;</span><br><span>@@ -418,11 +428,11 @@</span><br><span> </span><br><span>        osmo_fd_setup(&osmux_fd, -1, OSMO_FD_READ, osmux_read_fd_cb, cfg, 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port,</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = mgcp_create_bind(g_osmux->osmux_addr, &osmux_fd, g_osmux->osmux_port,</span><br><span>                                cfg->endp_dscp, cfg->endp_priority);</span><br><span>   if (ret < 0) {</span><br><span>            LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                    cfg->osmux_addr, cfg->osmux_port);</span><br><span style="color: hsl(120, 100%, 40%);">+              g_osmux->osmux_addr, g_osmux->osmux_port);</span><br><span>                return ret;</span><br><span>  }</span><br><span> </span><br><span>@@ -432,7 +442,7 @@</span><br><span>               osmo_sock_get_name2(osmux_fd.fd));</span><br><span>              return ret;</span><br><span>  }</span><br><span style="color: hsl(0, 100%, 40%);">-       cfg->osmux_init = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       g_osmux->osmux_init = 1;</span><br><span> </span><br><span>      LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n",</span><br><span>           osmo_sock_get_name2(osmux_fd.fd));</span><br><span>@@ -460,7 +470,7 @@</span><br><span>     */</span><br><span>  struct in6_addr addr_unset = {};</span><br><span>     static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-       uint16_t osmux_dummy = endp->trunk->cfg->osmux_dummy;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint16_t osmux_dummy = g_osmux->osmux_dummy;</span><br><span> </span><br><span>  /* Check if osmux is enabled for the specified connection */</span><br><span>         if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c</span><br><span>index f9ae2ec..1a39827 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_protocol.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_protocol.c</span><br><span>@@ -24,6 +24,7 @@</span><br><span> #include <ctype.h></span><br><span> #include <stdio.h></span><br><span> #include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span> #include <time.h></span><br><span> #include <limits.h></span><br><span> #include <unistd.h></span><br><span>@@ -46,6 +47,7 @@</span><br><span> #include <osmocom/mgcp/mgcp_sdp.h></span><br><span> #include <osmocom/mgcp/mgcp_codec.h></span><br><span> #include <osmocom/mgcp/mgcp_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span> </span><br><span> /* Contains the last successfully resolved endpoint name. This variable is used</span><br><span>  * for the unit-tests to verify that the endpoint was correctly resolved. */</span><br><span>@@ -67,27 +69,6 @@</span><br><span>             LOGPTRUNK(trunk, cat, level, fmt, ## args); \</span><br><span> } while (0)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* Request data passed to the request handler */</span><br><span style="color: hsl(0, 100%, 40%);">-struct mgcp_request_data {</span><br><span style="color: hsl(0, 100%, 40%);">-      /* request name (e.g. "MDCX") */</span><br><span style="color: hsl(0, 100%, 40%);">-      char name[4+1];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* parsing results from the MGCP header (trans id, endpoint name ...) */</span><br><span style="color: hsl(0, 100%, 40%);">-        struct mgcp_parse_data *pdata;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  /* pointer to endpoint resource (may be NULL for wildcarded requests) */</span><br><span style="color: hsl(0, 100%, 40%);">-        struct mgcp_endpoint *endp;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     /* pointer to trunk resource */</span><br><span style="color: hsl(0, 100%, 40%);">- struct mgcp_trunk *trunk;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* set to true when the request has been classified as wildcarded */</span><br><span style="color: hsl(0, 100%, 40%);">-    bool wildcarded;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        /* contains cause code in case of problems during endp/trunk resolution */</span><br><span style="color: hsl(0, 100%, 40%);">-      int mgcp_cause;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /* Request handler specification, here we specify an array with function</span><br><span>  * pointers to the various MGCP requests implemented below */</span><br><span> struct mgcp_request {</span><br><span>@@ -202,7 +183,7 @@</span><br><span>        return msg;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_resp(void* msgctx, struct mgcp_endpoint *endp, int code,</span><br><span>                               const char *txt, const char *msg,</span><br><span>                            const char *trans, const char *param,</span><br><span>                                const char *sdp)</span><br><span>@@ -210,7 +191,8 @@</span><br><span>       int len;</span><br><span>     struct msgb *res;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   res = mgcp_msgb_alloc(endp->trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(msgctx != 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     res = mgcp_msgb_alloc(msgctx);</span><br><span>       if (!res)</span><br><span>            return NULL;</span><br><span> </span><br><span>@@ -242,26 +224,26 @@</span><br><span>     return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_ok_resp_with_param(void* msgctx, struct mgcp_endpoint *endp,</span><br><span>                                             int code, const char *msg,</span><br><span>                                           const char *trans,</span><br><span>                                           const char *param)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   return create_resp(endp, code, " OK", msg, trans, param, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct msgb *create_ok_response(struct mgcp_endpoint *endp,</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_ok_response(void* msgctx, struct mgcp_endpoint *endp,</span><br><span>                                      int code, const char *msg,</span><br><span>                                   const char *trans)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  return create_ok_resp_with_param(endp, code, msg, trans, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct msgb *create_err_response(struct mgcp_endpoint *endp,</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_err_response(void* msgctx, struct mgcp_endpoint *endp,</span><br><span>                                       int code, const char *msg,</span><br><span>                                   const char *trans)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);</span><br><span> }</span><br><span> </span><br><span> /* Format MGCP response string (with SDP attached) */</span><br><span>@@ -318,7 +300,7 @@</span><br><span>   rc = mgcp_write_response_sdp(endp, conn, sdp, addr);</span><br><span>         if (rc < 0)</span><br><span>               goto error;</span><br><span style="color: hsl(0, 100%, 40%);">-     result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);</span><br><span style="color: hsl(120, 100%, 40%);">+  result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);</span><br><span>         msgb_free(sdp);</span><br><span>      return result;</span><br><span> error:</span><br><span>@@ -336,89 +318,147 @@</span><br><span>            mgcp_send_dummy(endp, conn);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* handle incoming messages:</span><br><span style="color: hsl(0, 100%, 40%);">- *   - this can be a command (four letters, space, transaction id)</span><br><span style="color: hsl(0, 100%, 40%);">- *   - or a response (three numbers, space, transaction id) */</span><br><span style="color: hsl(0, 100%, 40%);">-struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* partially sanitize and parse incoming message</span><br><span style="color: hsl(120, 100%, 40%);">+ * !! only public for testing !! */</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct to_trunkthread_mgcp_msg* w)</span><br><span> {</span><br><span>     struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct mgcp_parse_data pdata;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct mgcp_request_data rq;</span><br><span style="color: hsl(0, 100%, 40%);">-    int rc, i, code, handled = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct msgb *resp = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-       char *data;</span><br><span style="color: hsl(120, 100%, 40%);">+   int code;</span><br><span style="color: hsl(120, 100%, 40%);">+     ssize_t rc = w->x.msglen;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        debug_last_endpoint_name[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+   #define pdata w->x.pdata</span><br><span style="color: hsl(120, 100%, 40%);">+   #define rq w->x.rq</span><br><span> </span><br><span>    /* Count all messages, even incorect ones */</span><br><span>         rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (msgb_l2len(msg) < 4) {</span><br><span style="color: hsl(0, 100%, 40%);">-           LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc < sizeof(rq.name)-1) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLMGCP, LOGL_ERROR, "msg too short: %zd\n", rc);</span><br><span>              rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+     memcpy(rq.name, (const char *)&w->msg[0], sizeof(rq.name)-1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (mgcp_msg_terminate_nul(msg)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");</span><br><span style="color: hsl(120, 100%, 40%);">+    mgcp_disp_msg(w->msg, rc, "Received message");</span><br><span> </span><br><span>      /* attempt to treat it as a response */</span><br><span style="color: hsl(0, 100%, 40%);">- if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sscanf((const char *)&w->msg[0], "%3d %*s", &code) == 1) {</span><br><span>          LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);</span><br><span>            rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        /* Parse message, extract endpoint name and transaction identifier and request name etc. */</span><br><span style="color: hsl(0, 100%, 40%);">-     memset(&pdata, 0, sizeof(pdata));</span><br><span style="color: hsl(0, 100%, 40%);">-   memset(&rq, 0, sizeof(rq));</span><br><span style="color: hsl(0, 100%, 40%);">- pdata.cfg = cfg;</span><br><span style="color: hsl(0, 100%, 40%);">-        memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);</span><br><span style="color: hsl(0, 100%, 40%);">-  msg->l3h = &msg->l2h[4];</span><br><span style="color: hsl(0, 100%, 40%);">-      data = mgcp_strline((char *)msg->l3h, &pdata.save);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = mgcp_parse_header(&pdata, data);</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = mgcp_parse_header(&pdata, mgcp_strline((char *)&w->msg[4], &pdata.save));</span><br><span>        if (rc < 0) {</span><br><span>             LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);</span><br><span>           rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));</span><br><span style="color: hsl(0, 100%, 40%);">-                return create_err_response(NULL, -rc, rq.name, "000000");</span><br><span style="color: hsl(120, 100%, 40%);">+           return create_err_response(cfg, NULL, -rc, rq.name, "000000");</span><br><span>     }</span><br><span> </span><br><span>        /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */</span><br><span style="color: hsl(0, 100%, 40%);">-      rq.pdata = &pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+        rq.pdatap = &pdata;</span><br><span>      rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);</span><br><span style="color: hsl(0, 100%, 40%);">-  rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);</span><br><span style="color: hsl(0, 100%, 40%);">-  rq.mgcp_cause = rc;</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!rq.endp) {</span><br><span style="color: hsl(0, 100%, 40%);">-         rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));</span><br><span style="color: hsl(0, 100%, 40%);">-              if (rq.wildcarded) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    /* If we are unable to find the endpoint we still may be able to identify the trunk. Some</span><br><span style="color: hsl(0, 100%, 40%);">-                        * request handlers will still be able to perform a useful action if the request refers to</span><br><span style="color: hsl(0, 100%, 40%);">-                       * the whole trunk (wildcarded request). */</span><br><span style="color: hsl(0, 100%, 40%);">-                     LOGP(DLMGCP, LOGL_NOTICE,</span><br><span style="color: hsl(0, 100%, 40%);">-                            "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,</span><br><span style="color: hsl(0, 100%, 40%);">-                      pdata.epname, -rq.mgcp_cause);</span><br><span style="color: hsl(0, 100%, 40%);">-                     rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (!rq.trunk) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                  rq.name, pdata.epname);</span><br><span style="color: hsl(0, 100%, 40%);">-                            return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span style="color: hsl(120, 100%, 40%);">+       rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!rq.trunk) {</span><br><span style="color: hsl(120, 100%, 40%);">+              rq.mgcp_cause = -500; /* if we can't even find the trunk we also found no EP */</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                           rq.name, pdata.epname);</span><br><span style="color: hsl(120, 100%, 40%);">+               return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   w->x.successfully_parsed = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#undef pdata</span><br><span style="color: hsl(120, 100%, 40%);">+#undef rq</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* submit partially parsed message to thunkthreads */</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *mgcp_submit_message_to_trunkthread(struct mgcp_config *cfg, struct to_trunkthread_mgcp_msg* w)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct msgb * retmsg;</span><br><span style="color: hsl(120, 100%, 40%);">+ int which_thread = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    #define pdata w->x.pdata</span><br><span style="color: hsl(120, 100%, 40%);">+   #define rq w->x.rq</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       retmsg = mgcp_handle_message(cfg, w);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (retmsg || !w->x.successfully_parsed)</span><br><span style="color: hsl(120, 100%, 40%);">+           return retmsg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!rq.trunk->use_threads)</span><br><span style="color: hsl(120, 100%, 40%);">+                return thread_handle_mgcp_message(w, rq.trunk->thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* might not be wildcarded -> look up the thread according to ep name */</span><br><span style="color: hsl(120, 100%, 40%);">+   which_thread = get_trunk_thread_for_ep_name(pdata.epname, rq.trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* update pointers to offsets for interthread q */</span><br><span style="color: hsl(120, 100%, 40%);">+    pdata.trans = (char *)(pdata.trans - &w->msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+      pdata.epname = (char *)(pdata.epname - &w->msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (pdata.save >= &w->msg[0]) /* might be 0 in case of header-only messages, so be careful */</span><br><span style="color: hsl(120, 100%, 40%);">+               pdata.save = (char *)(pdata.save - &w->msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (rq.wildcarded) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (strcmp(rq.name, "CRCX") == 0){ /* crcx -> pick ONE thread that has free endpoints */</span><br><span style="color: hsl(120, 100%, 40%);">+                 for (int i = 0; i < rq.trunk->num_threads; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               if (rq.trunk->thread_info[i].eps_free) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                   thread_push_msg(rq.trunk, i, w);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                          }</span><br><span>                    }</span><br><span style="color: hsl(0, 100%, 40%);">-               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* just bother t0 to generate a nice error response if we don't have any eps left */</span><br><span style="color: hsl(120, 100%, 40%);">+                      thread_push_msg(rq.trunk, 0, w);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          } else { /* wildcarded, and not crcx -> to all threads of a trunk */</span><br><span style="color: hsl(120, 100%, 40%);">+                       for (int i = 0; i < rq.trunk->num_threads; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+                         thread_push_msg(rq.trunk, i, w);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* we now know the trunk and the ep, so dispatch it */</span><br><span style="color: hsl(120, 100%, 40%);">+        thread_push_msg(rq.trunk, which_thread, w);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#undef pdata</span><br><span style="color: hsl(120, 100%, 40%);">+#undef rq</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb* thread_handle_mgcp_message(struct to_trunkthread_mgcp_msg* w, struct per_thread_info *thread_info) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = -500, handled = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct msgb *resp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct rate_ctr_group *rate_ctrs = w->x.rq.trunk->cfg->ratectr.mgcp_general_ctr_group;</span><br><span style="color: hsl(120, 100%, 40%);">+       #define pdata w->x.pdata</span><br><span style="color: hsl(120, 100%, 40%);">+   #define rq w->x.rq</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* finds us a free ep in case wildcarded / crcx</span><br><span style="color: hsl(120, 100%, 40%);">+               OR finds us a proper ep passed by name */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!rq.wildcarded || !strcmp(rq.name, "CRCX"))</span><br><span style="color: hsl(120, 100%, 40%);">+             rq.endp = mgcp_endp_by_name_trunk(&rc, pdata.epname, rq.trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+   rq.mgcp_cause = rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rq.endp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                debug_last_endpoint_name[0] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!rq.endp) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (rq.wildcarded) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* we know this is the trunk that handles this wildcarded message */</span><br><span style="color: hsl(120, 100%, 40%);">+          } else  {</span><br><span style="color: hsl(120, 100%, 40%);">+                     rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));</span><br><span>                   /* If the endpoint name suggests that the request refers to a specific endpoint, then the</span><br><span style="color: hsl(0, 100%, 40%);">-                        * request cannot be handled and we must stop early. */</span><br><span style="color: hsl(120, 100%, 40%);">+                               * request cannot be handled and we must stop early. */</span><br><span>                       LOGP(DLMGCP, LOGL_NOTICE,</span><br><span style="color: hsl(0, 100%, 40%);">-                            "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,</span><br><span style="color: hsl(0, 100%, 40%);">-                            pdata.epname, -rq.mgcp_cause);</span><br><span style="color: hsl(0, 100%, 40%);">-                     return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                                       "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  pdata.epname, -rq.mgcp_cause);</span><br><span style="color: hsl(120, 100%, 40%);">+                        return create_err_response(rq.trunk, NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span>            }</span><br><span>    } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));</span><br><span style="color: hsl(0, 100%, 40%);">-             rq.trunk = rq.endp->trunk;</span><br><span>                rq.mgcp_cause = 0;</span><br><span> </span><br><span>               /* Check if we have to retransmit a response from a previous transaction */</span><br><span>@@ -429,16 +469,19 @@</span><br><span>  }</span><br><span> </span><br><span>        /* Find an appropriate handler for the current request and execute it */</span><br><span style="color: hsl(0, 100%, 40%);">-        for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+  for (int i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {</span><br><span>             if (strcmp(mgcp_requests[i].name, rq.name) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                  #if 0</span><br><span>                        /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't</span><br><span>                       * execute the request handler. */</span><br><span>                   if (mgcp_requests[i].require_endp && !rq.endp) {</span><br><span>                             LOGP(DLMGCP, LOGL_ERROR,</span><br><span>                                  "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",</span><br><span>                                  rq.name, mgcp_requests[i].debug_name, pdata.epname);</span><br><span style="color: hsl(0, 100%, 40%);">-                               return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                               return create_err_response(rq.trunk, NULL, -rq.mgcp_cause, rq.name, pdata.trans);</span><br><span>                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     #endif</span><br><span> </span><br><span>                   /* Execute request handler */</span><br><span>                        if (rq.endp)</span><br><span>@@ -455,22 +498,40 @@</span><br><span>                 }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Check if the MGCP request was handled and increment rate counters accordingly. */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (handled) {</span><br><span style="color: hsl(0, 100%, 40%);">-          rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));</span><br><span style="color: hsl(0, 100%, 40%);">-  } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+       /* ensure we do not send back duplicate messages due to wildcarded requests */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (thread_info->tid != 0 && rq.wildcarded && strcmp(rq.name, "CRCX")) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (resp)</span><br><span style="color: hsl(120, 100%, 40%);">+                     msgb_free(resp);</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check if the MGCP request was handled and increment rate counters accordingly - but don't count this multiple times */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!rq.wildcarded || (rq.wildcarded && !strcmp(rq.name, "CRCX"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (handled) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", rq.name);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span>    }</span><br><span> </span><br><span>        return resp;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#undef pdata</span><br><span style="color: hsl(120, 100%, 40%);">+#undef rq</span><br><span> </span><br><span> /* AUEP command handler, processes the received command */</span><br><span> static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)</span><br><span> {</span><br><span>         LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");</span><br><span style="color: hsl(0, 100%, 40%);">-  return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!rq->endp || !mgcp_endp_avail(rq->endp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                      "AUEP: selected endpoint not available!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdatap->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdatap->trans);</span><br><span> }</span><br><span> </span><br><span> /* Try to find a free port by attempting to bind on it. Also handle the</span><br><span>@@ -750,7 +811,7 @@</span><br><span>  */</span><br><span> static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!endp->trunk->cfg->osmux_init) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (! osmux_get_global_opt()->osmux_init) {</span><br><span>               if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {</span><br><span>                     LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");</span><br><span>                         return -3;</span><br><span>@@ -779,7 +840,7 @@</span><br><span>             /* If we have SDP, we ignore the local connection options and</span><br><span>                 * use only the SDP information. */</span><br><span>          mgcp_codec_reset_all(conn);</span><br><span style="color: hsl(0, 100%, 40%);">-             rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = mgcp_parse_sdp_data(endp, conn, rq->pdatap);</span><br><span>                 if (rc != 0) {</span><br><span>                       LOGPCONN(conn->conn, DLMGCP,  LOGL_ERROR,</span><br><span>                                  "%s: sdp not parseable\n", cmd);</span><br><span>@@ -847,7 +908,7 @@</span><br><span> /* CRCX command handler, processes the received command */</span><br><span> static struct msgb *handle_create_con(struct mgcp_request_data *rq)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     struct mgcp_parse_data *pdata = rq->pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_parse_data *pdata = rq->pdatap;</span><br><span>       struct mgcp_trunk *trunk = rq->trunk;</span><br><span>     struct mgcp_endpoint *endp = rq->endp;</span><br><span>    struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;</span><br><span>@@ -864,11 +925,19 @@</span><br><span> </span><br><span>        LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     /* we must have a free ep */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!endp) {</span><br><span style="color: hsl(120, 100%, 40%);">+          rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                     "CRCX: no free endpoints available!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!mgcp_endp_avail(endp)) {</span><br><span>                rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));</span><br><span>               LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "CRCX: selected endpoint not available!\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         return create_err_response(NULL, 501, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        /* parse CallID C: and LocalParameters L: */</span><br><span>@@ -888,7 +957,7 @@</span><br><span>                    * together with a CRCX, the MGW will assign the</span><br><span>                      * connection identifier by itself on CRCX */</span><br><span>                        rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));</span><br><span style="color: hsl(0, 100%, 40%);">-                     return create_err_response(NULL, 523, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);</span><br><span>                      break;</span><br><span>               case 'M':</span><br><span>                    mode = (const char *)line + 3;</span><br><span>@@ -896,7 +965,7 @@</span><br><span>                 case 'X':</span><br><span>                    if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {</span><br><span>                          /* If osmux is disabled, just skip setting it up */</span><br><span style="color: hsl(0, 100%, 40%);">-                             if (!rq->endp->trunk->cfg->osmux)</span><br><span style="color: hsl(120, 100%, 40%);">+                         if (! trunk->cfg->global_osmux_options.osmux)</span><br><span>                                  break;</span><br><span>                               osmux_cid = mgcp_osmux_setup(endp, line);</span><br><span>                            break;</span><br><span>@@ -914,7 +983,7 @@</span><br><span>                         LOGPENDP(endp, DLMGCP, LOGL_NOTICE,</span><br><span>                           "CRCX: unhandled option: '%c'/%d\n", *line, *line);</span><br><span>                       rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));</span><br><span style="color: hsl(0, 100%, 40%);">-                        return create_err_response(NULL, 539, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);</span><br><span>                      break;</span><br><span>               }</span><br><span>    }</span><br><span>@@ -925,14 +994,14 @@</span><br><span>            LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "CRCX: insufficient parameters, missing callid\n");</span><br><span>               rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));</span><br><span style="color: hsl(0, 100%, 40%);">-         return create_err_response(endp, 516, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 516, "CRCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        if (!mode) {</span><br><span>                 LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "CRCX: insufficient parameters, missing mode\n");</span><br><span>                 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));</span><br><span style="color: hsl(0, 100%, 40%);">-           return create_err_response(endp, 517, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 517, "CRCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        /* Check if we are able to accept the creation of another connection */</span><br><span>@@ -949,7 +1018,7 @@</span><br><span>                       /* There is no more room for a connection, leave</span><br><span>                      * everything as it is and return with an error */</span><br><span>                   rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));</span><br><span style="color: hsl(0, 100%, 40%);">-                 return create_err_response(endp, 540, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(endp, endp, 540, "CRCX", pdata->trans);</span><br><span>              }</span><br><span>    }</span><br><span> </span><br><span>@@ -967,7 +1036,7 @@</span><br><span>                         /* This is not our call, leave everything as it is and</span><br><span>                        * return with an error. */</span><br><span>                  rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));</span><br><span style="color: hsl(0, 100%, 40%);">-                 return create_err_response(endp, 400, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(endp, endp, 400, "CRCX", pdata->trans);</span><br><span>              }</span><br><span>    }</span><br><span> </span><br><span>@@ -978,7 +1047,7 @@</span><br><span>                 rc = mgcp_endp_claim(endp, callid);</span><br><span>          if (rc != 0) {</span><br><span>                       rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));</span><br><span style="color: hsl(0, 100%, 40%);">-                  return create_err_response(endp, 502, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(endp, endp, 502, "CRCX", pdata->trans);</span><br><span>              }</span><br><span>    }</span><br><span> </span><br><span>@@ -1010,7 +1079,7 @@</span><br><span>                        rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));</span><br><span>                    goto error2;</span><br><span>                 }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (osmux_get_global_opt()->osmux == OSMUX_USAGE_ONLY) {</span><br><span>           LOGPCONN(_conn, DLMGCP, LOGL_ERROR,</span><br><span>                   "CRCX: osmux only and no osmux offered\n");</span><br><span>               rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));</span><br><span>@@ -1097,13 +1166,13 @@</span><br><span>  mgcp_endp_release(endp);</span><br><span>     LOGPENDP(endp, DLMGCP, LOGL_NOTICE,</span><br><span>           "CRCX: unable to create connection\n");</span><br><span style="color: hsl(0, 100%, 40%);">-      return create_err_response(endp, error_code, "CRCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+      return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);</span><br><span> }</span><br><span> </span><br><span> /* MDCX command handler, processes the received command */</span><br><span> static struct msgb *handle_modify_con(struct mgcp_request_data *rq)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct mgcp_parse_data *pdata = rq->pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_parse_data *pdata = rq->pdatap;</span><br><span>       struct mgcp_trunk *trunk = rq->trunk;</span><br><span>     struct mgcp_endpoint *endp = rq->endp;</span><br><span>    struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;</span><br><span>@@ -1121,26 +1190,26 @@</span><br><span> </span><br><span>      LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (!mgcp_endp_avail(endp)) {</span><br><span style="color: hsl(0, 100%, 40%);">-           rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span style="color: hsl(0, 100%, 40%);">-                       "MDCX: selected endpoint not available!\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         return create_err_response(NULL, 501, "MDCX", pdata->trans);</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* Prohibit wildcarded requests */</span><br><span>   if (rq->wildcarded) {</span><br><span>             LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "MDCX: wildcarded endpoint names not supported.\n");</span><br><span>              rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));</span><br><span style="color: hsl(0, 100%, 40%);">-               return create_err_response(endp, 507, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!endp || !mgcp_endp_avail(endp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                     "MDCX: selected endpoint not available!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        if (llist_count(&endp->conns) <= 0) {</span><br><span>              LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "MDCX: endpoint is not holding a connection.\n");</span><br><span>                 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));</span><br><span style="color: hsl(0, 100%, 40%);">-                return create_err_response(endp, 400, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 400, "MDCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        for_each_line(line, pdata->save) {</span><br><span>@@ -1174,7 +1243,7 @@</span><br><span>                case 'X':</span><br><span>                    if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {</span><br><span>                          /* If osmux is disabled, just skip setting it up */</span><br><span style="color: hsl(0, 100%, 40%);">-                             if (!endp->trunk->cfg->osmux)</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (!osmux_get_global_opt()->osmux)</span><br><span>                                       break;</span><br><span>                               osmux_cid = mgcp_osmux_setup(endp, line);</span><br><span>                            break;</span><br><span>@@ -1190,7 +1259,7 @@</span><br><span>                                "MDCX: Unhandled MGCP option: '%c'/%d\n",</span><br><span>                                  line[0], line[0]);</span><br><span>                  rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));</span><br><span style="color: hsl(0, 100%, 40%);">-                        return create_err_response(NULL, 539, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);</span><br><span>                      break;</span><br><span>               }</span><br><span>    }</span><br><span>@@ -1200,13 +1269,13 @@</span><br><span>          LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");</span><br><span>            rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));</span><br><span style="color: hsl(0, 100%, 40%);">-              return create_err_response(endp, 515, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 515, "MDCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        conn = mgcp_conn_get_rtp(endp, conn_id);</span><br><span>     if (!conn) {</span><br><span>                 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));</span><br><span style="color: hsl(0, 100%, 40%);">-         return create_err_response(endp, 400, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 400, "MDCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        mgcp_conn_watchdog_kick(conn->conn);</span><br><span>@@ -1313,7 +1382,7 @@</span><br><span>      mgcp_endp_update(endp);</span><br><span>      return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);</span><br><span> error3:</span><br><span style="color: hsl(0, 100%, 40%);">-        return create_err_response(endp, error_code, "MDCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+      return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);</span><br><span> </span><br><span> out_silent:</span><br><span>    LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");</span><br><span>@@ -1323,7 +1392,7 @@</span><br><span> /* DLCX command handler, processes the received command */</span><br><span> static struct msgb *handle_delete_con(struct mgcp_request_data *rq)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  struct mgcp_parse_data *pdata = rq->pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_parse_data *pdata = rq->pdatap;</span><br><span>       struct mgcp_trunk *trunk = rq->trunk;</span><br><span>     struct mgcp_endpoint *endp = rq->endp;</span><br><span>    struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;</span><br><span>@@ -1344,14 +1413,27 @@</span><br><span>          rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));</span><br><span>               LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "DLCX: selected endpoint not available!\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         return create_err_response(NULL, 501, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {</span><br><span>                LOGPENDP(endp, DLMGCP, LOGL_ERROR,</span><br><span>                    "DLCX: endpoint is not holding a connection.\n");</span><br><span>                 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));</span><br><span style="color: hsl(0, 100%, 40%);">-                return create_err_response(endp, 515, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+             return create_err_response(endp, endp, 515, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Handle wildcarded DLCX that refers to the whole trunk. This means</span><br><span style="color: hsl(120, 100%, 40%);">+   * that we walk over all endpoints on the trunk in order to drop all</span><br><span style="color: hsl(120, 100%, 40%);">+   * connections on the trunk. (see also RFC3435 Annex F.7) */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rq->wildcarded) {</span><br><span style="color: hsl(120, 100%, 40%);">+              int num_conns = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            for (i = 0; i < trunk->number_endpoints; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 num_conns += llist_count(&trunk->endpoints[i]->conns);</span><br><span style="color: hsl(120, 100%, 40%);">+                      mgcp_endp_release(trunk->endpoints[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);</span><br><span style="color: hsl(120, 100%, 40%);">+                return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);</span><br><span>      }</span><br><span> </span><br><span>        for_each_line(line, pdata->save) {</span><br><span>@@ -1364,9 +1446,9 @@</span><br><span>                           then this request cannot be handled */</span><br><span>                    if (!endp) {</span><br><span>                                 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     "cannot handle requests with call-id (C) without endpoint -- abort!");</span><br><span style="color: hsl(120, 100%, 40%);">+                                            "cannot handle requests with call-id (C) without endpoint -- abort!");</span><br><span>                             rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));</span><br><span style="color: hsl(0, 100%, 40%);">-                                return create_err_response(NULL, 539, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                             return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);</span><br><span>                      }</span><br><span> </span><br><span>                        if (mgcp_verify_call_id(endp, line + 3) != 0) {</span><br><span>@@ -1380,9 +1462,9 @@</span><br><span>                         then this request cannot be handled */</span><br><span>                    if (!endp) {</span><br><span>                                 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     "cannot handle requests with conn-id (I) without endpoint -- abort!");</span><br><span style="color: hsl(120, 100%, 40%);">+                                    "cannot handle requests with conn-id (I) without endpoint -- abort!");</span><br><span>                             rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));</span><br><span style="color: hsl(0, 100%, 40%);">-                                return create_err_response(NULL, 539, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                             return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);</span><br><span>                      }</span><br><span> </span><br><span>                        conn_id = (const char *)line + 3;</span><br><span>@@ -1398,24 +1480,11 @@</span><br><span>                  LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",</span><br><span>                                line[0], line[0]);</span><br><span>                  rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));</span><br><span style="color: hsl(0, 100%, 40%);">-                        return create_err_response(NULL, 539, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);</span><br><span>                      break;</span><br><span>               }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Handle wildcarded DLCX that refers to the whole trunk. This means</span><br><span style="color: hsl(0, 100%, 40%);">-     * that we walk over all endpoints on the trunk in order to drop all</span><br><span style="color: hsl(0, 100%, 40%);">-     * connections on the trunk. (see also RFC3435 Annex F.7) */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (rq->wildcarded) {</span><br><span style="color: hsl(0, 100%, 40%);">-                int num_conns = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-              for (i = 0; i < trunk->number_endpoints; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   num_conns += llist_count(&trunk->endpoints[i]->conns);</span><br><span style="color: hsl(0, 100%, 40%);">-                        mgcp_endp_release(trunk->endpoints[i]);</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-               rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);</span><br><span style="color: hsl(0, 100%, 40%);">-          return create_ok_response(NULL, 200, "DLCX", pdata->trans);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* The logic does not permit to go past this point without having the</span><br><span>         * the endp pointer populated. */</span><br><span>    OSMO_ASSERT(endp);</span><br><span>@@ -1438,7 +1507,7 @@</span><br><span>           /* Note: In this case we do not return any statistics,</span><br><span>                * as we assume that the client is not interested in</span><br><span>                  * this case. */</span><br><span style="color: hsl(0, 100%, 40%);">-                return create_ok_response(endp, 200, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+              return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);</span><br><span>       }</span><br><span> </span><br><span>        /* Find the connection */</span><br><span>@@ -1467,10 +1536,10 @@</span><br><span>  rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));</span><br><span>  if (silent)</span><br><span>          goto out_silent;</span><br><span style="color: hsl(0, 100%, 40%);">-        return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);</span><br><span style="color: hsl(120, 100%, 40%);">+        return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);</span><br><span> </span><br><span> error3:</span><br><span style="color: hsl(0, 100%, 40%);">-     return create_err_response(endp, error_code, "DLCX", pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+      return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);</span><br><span> </span><br><span> out_silent:</span><br><span>    LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");</span><br><span>@@ -1488,10 +1557,15 @@</span><br><span>        * mechanism to distinguish which endpoint shall be resetted</span><br><span>          * is needed */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   LOGP(DLMGCP, LOGL_NOTICE, "Asked to reset endpoints: %u/%d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      rq->trunk->trunk_nr, rq->trunk->trunk_type);</span><br><span>     LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (rq->pdata->cfg->reset_cb)</span><br><span style="color: hsl(0, 100%, 40%);">-          rq->pdata->cfg->reset_cb(rq->endp->trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Walk over all endpoints and trigger a release, this will release all</span><br><span style="color: hsl(120, 100%, 40%);">+               * endpoints, possible open connections are forcefully dropped */</span><br><span style="color: hsl(120, 100%, 40%);">+      for (int i = 0; i < rq->trunk->number_endpoints; ++i)</span><br><span style="color: hsl(120, 100%, 40%);">+                mgcp_endp_release(rq->trunk->endpoints[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return NULL;</span><br><span> }</span><br><span> </span><br><span>@@ -1515,7 +1589,7 @@</span><br><span> </span><br><span>    LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     for_each_line(line, rq->pdata->save) {</span><br><span style="color: hsl(120, 100%, 40%);">+  for_each_line(line, rq->pdatap->save) {</span><br><span>                switch (toupper(line[0])) {</span><br><span>          case 'S':</span><br><span>                    tone = extract_tone(line);</span><br><span>@@ -1525,14 +1599,14 @@</span><br><span> </span><br><span>     /* we didn't see a signal request with a tone */</span><br><span>         if (tone == CHAR_MAX)</span><br><span style="color: hsl(0, 100%, 40%);">-           return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+                return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdatap->trans);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (rq->pdata->cfg->rqnt_cb)</span><br><span style="color: hsl(0, 100%, 40%);">-           res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (rq->pdatap->cfg->rqnt_cb)</span><br><span style="color: hsl(120, 100%, 40%);">+                res = rq->pdatap->cfg->rqnt_cb(rq->endp, tone);</span><br><span> </span><br><span>      return res == 0 ?</span><br><span style="color: hsl(0, 100%, 40%);">-           create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :</span><br><span style="color: hsl(0, 100%, 40%);">-        create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);</span><br><span style="color: hsl(120, 100%, 40%);">+      create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdatap->trans) :</span><br><span style="color: hsl(120, 100%, 40%);">+        create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdatap->trans);</span><br><span> }</span><br><span> </span><br><span> /* Connection keepalive timer, will take care that dummy packets are send</span><br><span>@@ -1618,14 +1692,14 @@</span><br><span> </span><br><span>       osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      cfg->net_ports.lock = PTHREAD_MUTEX_INITIALIZER;</span><br><span style="color: hsl(120, 100%, 40%);">+   cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;</span><br><span>         cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;</span><br><span>        cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;</span><br><span>    cfg->net_ports.last_port = cfg->net_ports.range_start;</span><br><span> </span><br><span>     cfg->source_port = 2427;</span><br><span>  osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_strlcpy(cfg->global_osmux_options.osmux_addr, "0.0.0.0", sizeof(cfg->global_osmux_options.osmux_addr));</span><br><span> </span><br><span>     cfg->rtp_processing_cb = &mgcp_rtp_processing_default;</span><br><span>        cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c</span><br><span>index 801ae35..27d24fe 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_sdp.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_sdp.c</span><br><span>@@ -601,7 +601,7 @@</span><br><span> </span><br><span>                 payload_types[0] = payload_type;</span><br><span>             if (mgcp_conn_rtp_is_osmux(conn))</span><br><span style="color: hsl(0, 100%, 40%);">-                       local_port = endp->trunk->cfg->osmux_port;</span><br><span style="color: hsl(120, 100%, 40%);">+                   local_port = osmux_get_global_opt()->osmux_port;</span><br><span>          else</span><br><span>                         local_port = conn->end.local_port;</span><br><span>                rc = add_audio(sdp, payload_types, 1, local_port);</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_stat.c b/src/libosmo-mgcp/mgcp_stat.c</span><br><span>index 49493ce..bd5df51 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_stat.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_stat.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> #include <osmocom/mgcp/mgcp_trunk.h></span><br><span> </span><br><span> /* Helper function for mgcp_format_stats_rtp() to calculate packet loss */</span><br><span style="color: hsl(120, 100%, 40%);">+__attribute__((no_sanitize("integer")))</span><br><span> void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)</span><br><span> {</span><br><span>     struct mgcp_rtp_state *state = &conn->state;</span><br><span>@@ -94,7 +95,7 @@</span><br><span>      str += nchars;</span><br><span>       str_len -= nchars;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {</span><br><span style="color: hsl(120, 100%, 40%);">+  if (osmux_get_global_opt()->osmux != OSMUX_USAGE_OFF) {</span><br><span>           /* Error Counter */</span><br><span>          nchars = snprintf(str, str_len,</span><br><span>                                "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_threads.c b/src/libosmo-mgcp/mgcp_threads.c</span><br><span>new file mode 100644</span><br><span>index 0000000..fe939b4</span><br><span>--- /dev/null</span><br><span>+++ b/src/libosmo-mgcp/mgcp_threads.c</span><br><span>@@ -0,0 +1,457 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Eric Wild</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pthread.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <assert.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/prctl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stat_item.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_trunk.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_endp.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/cpu_sched_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct log_info log_info = {};</span><br><span style="color: hsl(120, 100%, 40%);">+void __thread *top_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void thread_handle_vty_msg(struct to_trunkthread_cfg_msg *w, struct per_thread_info *thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+static ssize_t thread_wait_vty_cmd_response(struct mgcp_trunk *master_trunk, int i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define GETF(yy, zz)                                                                                                   \</span><br><span style="color: hsl(120, 100%, 40%);">+    static struct spsc *get_spsc_##yy##_##zz##_for_thread(struct mgcp_trunk *trunk, unsigned int threadnum)        \</span><br><span style="color: hsl(120, 100%, 40%);">+      {                                                                                                              \</span><br><span style="color: hsl(120, 100%, 40%);">+              return trunk->thread_info[threadnum].chan_##yy.zz;                                                     \</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+GETF(cfg, a)</span><br><span style="color: hsl(120, 100%, 40%);">+GETF(cfg, b)</span><br><span style="color: hsl(120, 100%, 40%);">+GETF(mgcp, a)</span><br><span style="color: hsl(120, 100%, 40%);">+GETF(mgcp, b)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#undef GETFUN</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_push_msg(struct mgcp_trunk *trunk, unsigned int threadnum, void *elem)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       while (!spsc_push(get_spsc_mgcp_a_for_thread(trunk, threadnum), elem))</span><br><span style="color: hsl(120, 100%, 40%);">+                ;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void _thread_dispatch_ep_msg(struct mgcp_trunk *master_trunk, int threadnum, struct to_trunkthread_cfg_msg *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      /* this trunk does not have threads yet - we're parsing the config file */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!master_trunk->thread_info)</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!master_trunk->use_threads) {</span><br><span style="color: hsl(120, 100%, 40%);">+          thread_handle_vty_msg(m, master_trunk->thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (!spsc_push(get_spsc_cfg_a_for_thread(master_trunk, threadnum), m))</span><br><span style="color: hsl(120, 100%, 40%);">+             ;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   thread_wait_vty_cmd_response(master_trunk, threadnum);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_dispatch_ep_msg(struct mgcp_trunk *master_trunk, int threadnum, struct vty *v,</span><br><span style="color: hsl(120, 100%, 40%);">+                           enum trunkthread_cfg_msg_t ty, struct to_trunkthread_cfg_msg *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(m->type != IS_INVALID);</span><br><span style="color: hsl(120, 100%, 40%);">+        m->type = ty;</span><br><span style="color: hsl(120, 100%, 40%);">+      m->vty = v;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      _thread_dispatch_ep_msg(master_trunk, threadnum, m);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! send vty message to a trunks thread.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] trunk master trunk.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] threadnum thread number.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] vty vty struct.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] epname epname or NULL for all endpoints.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] show_stats show stats as well.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] active_only only show active endpoint(s). */</span><br><span style="color: hsl(120, 100%, 40%);">+void thread_dispatch_ep_dump_msg(struct mgcp_trunk *master_trunk, int threadnum, struct vty *vty, const char *epname,</span><br><span style="color: hsl(120, 100%, 40%);">+                           bool show_stats, bool active_only)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct to_trunkthread_cfg_msg m = { .type = IS_VTYMSG,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            .vty = vty,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           .v.epname = { 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+                                            .v.show_stats = show_stats,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           .v.active_only = active_only };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (epname)</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_strlcpy(m.v.epname, epname, sizeof(m.v.epname));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       _thread_dispatch_ep_msg(master_trunk, threadnum, &m);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! wait for a trunks thread to respond to a previous cfg message. */</span><br><span style="color: hsl(120, 100%, 40%);">+static ssize_t thread_wait_vty_cmd_response(struct mgcp_trunk *master_trunk, int i)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct to_trunkthread_cfg_msg w;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!master_trunk->use_threads)</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ssize_t rv = prep_pop(get_spsc_cfg_b_for_thread(master_trunk, i));</span><br><span style="color: hsl(120, 100%, 40%);">+    while (spsc_pop(get_spsc_cfg_b_for_thread(master_trunk, i), &w))</span><br><span style="color: hsl(120, 100%, 40%);">+          ;</span><br><span style="color: hsl(120, 100%, 40%);">+     return rv;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int map_ep_num_to_thread(struct mgcp_trunk *thread_parent_trunk, unsigned int which_epnum)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int epnum_total = thread_parent_trunk->v.vty_number_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+     int num_threads_total = thread_parent_trunk->num_threads;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        which_epnum = which_epnum >= epnum_total ? epnum_total - 1 : which_epnum;</span><br><span style="color: hsl(120, 100%, 40%);">+  int a = epnum_total / num_threads_total;</span><br><span style="color: hsl(120, 100%, 40%);">+      int b = epnum_total % num_threads_total;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    int rv = (a + 1) * b > which_epnum ? which_epnum / (a + 1) : (which_epnum - (b * (a + 1))) / a + b;</span><br><span style="color: hsl(120, 100%, 40%);">+        return rv;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int get_trunk_thread_for_ep_name(const char *epname, struct mgcp_trunk *thread_parent_trunk)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char epname_stripped[MGCP_ENDPOINT_MAXLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+   if (thread_parent_trunk->trunk_type == MGCP_TRUNK_E1)</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mgcp_endp_strip_name(epname_stripped, epname, thread_parent_trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int epnum = strtol(epname_stripped, NULL, 16);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* virtual trunk ep nums start with 1, split begins at index 0*/</span><br><span style="color: hsl(120, 100%, 40%);">+      epnum = epnum > 0 ? epnum - 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ return map_ep_num_to_thread(thread_parent_trunk, epnum);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void get_ep_num_per_thread(struct mgcp_trunk *parent_trunk, int which_threadnum, unsigned int *ep_per_thread,</span><br><span style="color: hsl(120, 100%, 40%);">+                     unsigned int *ep_start_offset)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int epnum_total = parent_trunk->v.vty_number_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+    int num_threads_total = parent_trunk->num_threads;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       int a = epnum_total / num_threads_total;</span><br><span style="color: hsl(120, 100%, 40%);">+      int b = epnum_total % num_threads_total;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    *ep_per_thread = which_threadnum < b ? a + 1 : a;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (which_threadnum <= b)</span><br><span style="color: hsl(120, 100%, 40%);">+          *ep_start_offset = which_threadnum * (a + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+          *ep_start_offset = (b) * (a + 1) + (which_threadnum - b) * a;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* it's fine to call this during cfg file parsing: num_threads is 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+static void send_async_vty_update_msg_to_trunk_threads(struct mgcp_config *c, struct mgcp_trunk *t)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct to_trunkthread_cfg_msg m = { 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+      m.type = IS_CFGMSG;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (t) {</span><br><span style="color: hsl(120, 100%, 40%);">+              m.c.content |= HAS_TRUNK;</span><br><span style="color: hsl(120, 100%, 40%);">+             m.c.t = *t;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (c) {</span><br><span style="color: hsl(120, 100%, 40%);">+              m.c.content |= HAS_CFG;</span><br><span style="color: hsl(120, 100%, 40%);">+               m.c.c = *c;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(m.c.content != 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* if we don't have a trunk then this is a cfg change affecting all trunks */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (t) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!t->thread_info) /* we're parsing the config file */</span><br><span style="color: hsl(120, 100%, 40%);">+                       return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!t->use_threads) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     thread_handle_vty_msg(&m, t->thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             for (int i = 0; i < t->num_threads; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+                        while (!spsc_push(get_spsc_cfg_a_for_thread(t, i), &m))</span><br><span style="color: hsl(120, 100%, 40%);">+                           ;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry (t, &c->trunks, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (!t->thread_info) /* we're parsing the config file */</span><br><span style="color: hsl(120, 100%, 40%);">+                               continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (!t->use_threads) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             thread_handle_vty_msg(&m, t->thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              for (int i = 0; i < t->num_threads; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+                                        while (!spsc_push(get_spsc_cfg_a_for_thread(t, i), &m))</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void send_async_vty_trunk_update_msg(struct mgcp_trunk *t)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_update_msg_to_trunk_threads(NULL, t);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void send_async_vty_cfg_update_msg(struct mgcp_config *c)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_update_msg_to_trunk_threads(c, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* just copy all settings, then sanitize them */</span><br><span style="color: hsl(120, 100%, 40%);">+static void update_local_trunk(struct per_thread_info *tinfo, struct mgcp_trunk *source_trunk)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct mgcp_trunk t, *dest_trunk = tinfo->this_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    t = *source_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+    t.cfg = tinfo->this_trunk->cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* keep this: own data! */</span><br><span style="color: hsl(120, 100%, 40%);">+    t.this_thread_info = dest_trunk->this_thread_info;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       t.keepalive_timer = dest_trunk->keepalive_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+   t.number_endpoints = dest_trunk->number_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+ t.number_endpoints_offset = dest_trunk->number_endpoints_offset;</span><br><span style="color: hsl(120, 100%, 40%);">+   t.endpoints = dest_trunk->endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+       t.ratectr = dest_trunk->ratectr;</span><br><span style="color: hsl(120, 100%, 40%);">+   t.stats = dest_trunk->stats;</span><br><span style="color: hsl(120, 100%, 40%);">+       t.num_threads = dest_trunk->num_threads;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dest_trunk->trunk_type == MGCP_TRUNK_VIRTUAL)</span><br><span style="color: hsl(120, 100%, 40%);">+          t.v = dest_trunk->v;</span><br><span style="color: hsl(120, 100%, 40%);">+       else</span><br><span style="color: hsl(120, 100%, 40%);">+          t.e1 = dest_trunk->e1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *dest_trunk = t;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (dest_trunk->keepalive_interval != source_trunk->keepalive_interval)</span><br><span style="color: hsl(120, 100%, 40%);">+         mgcp_trunk_set_keepalive(dest_trunk, dest_trunk->keepalive_interval);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* just copy all settings, then sanitize them */</span><br><span style="color: hsl(120, 100%, 40%);">+static void update_local_cfg(struct per_thread_info *tinfo, struct mgcp_config *source_cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct mgcp_config c;</span><br><span style="color: hsl(120, 100%, 40%);">+ c = *source_cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* no dynamic updates due to possible active threading! */</span><br><span style="color: hsl(120, 100%, 40%);">+    c.global_osmux_options.osmux = tinfo->this_trunk->cfg->global_osmux_options.osmux;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ensure our per-thread config copy only contains our own trunk </span><br><span style="color: hsl(120, 100%, 40%);">+             this is mostly to ensure the osmux trunk lookup works as-is */</span><br><span style="color: hsl(120, 100%, 40%);">+        INIT_LLIST_HEAD(&c.trunks);</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_add_tail(&tinfo->this_trunk->entry, &c.trunks);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ //FIXME: whole config?</span><br><span style="color: hsl(120, 100%, 40%);">+        *tinfo->this_trunk->cfg = c;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void handle_vty_cmd(struct per_thread_info *thread_info, struct to_trunkthread_cfg_msg *m);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void thread_handle_vty_msg(struct to_trunkthread_cfg_msg *w, struct per_thread_info *thread_info)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (w->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case IS_CFGMSG:</span><br><span style="color: hsl(120, 100%, 40%);">+               if (w->c.content & HAS_TRUNK)</span><br><span style="color: hsl(120, 100%, 40%);">+                  update_local_trunk(thread_info, &w->c.t);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (w->c.content & HAS_CFG)</span><br><span style="color: hsl(120, 100%, 40%);">+                    update_local_cfg(thread_info, &w->c.c);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case IS_VTYMSG:</span><br><span style="color: hsl(120, 100%, 40%);">+       case IS_FREEEPMSG:</span><br><span style="color: hsl(120, 100%, 40%);">+    case IS_TAPMSG:</span><br><span style="color: hsl(120, 100%, 40%);">+       case IS_LOOPMSG:</span><br><span style="color: hsl(120, 100%, 40%);">+              handle_vty_cmd(thread_info, w);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int thread_cfg_cb(struct osmo_fd *ofd, unsigned int what)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   uint64_t ev_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct per_thread_info *thread_info = (struct per_thread_info *)ofd->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));</span><br><span style="color: hsl(120, 100%, 40%);">+       assert(rc == sizeof(ev_cnt));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       struct to_trunkthread_cfg_msg w;</span><br><span style="color: hsl(120, 100%, 40%);">+      while (spsc_pop(thread_info->chan_cfg.a, &w)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                thread_handle_vty_msg(&w, thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int thread_cb(struct osmo_fd *ofd, unsigned int what)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint64_t ev_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct per_thread_info *thread_info = (struct per_thread_info *)ofd->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ int returnfd = thread_info->parent_trunk->cfg->gw_fd.bfd.fd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));</span><br><span style="color: hsl(120, 100%, 40%);">+       assert(rc == sizeof(ev_cnt));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       struct to_trunkthread_mgcp_msg w;</span><br><span style="color: hsl(120, 100%, 40%);">+     while (spsc_pop(thread_info->chan_mgcp.a, &w)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* update offsets to pointers for interthread q */</span><br><span style="color: hsl(120, 100%, 40%);">+            w.x.rq.pdatap = &w.x.pdata;</span><br><span style="color: hsl(120, 100%, 40%);">+               w.x.pdata.trans = (char *)((uintptr_t)w.x.pdata.trans + &w.msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+               w.x.pdata.epname = (char *)((uintptr_t)w.x.pdata.epname + &w.msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (w.x.pdata.save)</span><br><span style="color: hsl(120, 100%, 40%);">+                   w.x.pdata.save = (char *)((uintptr_t)w.x.pdata.save + &w.msg[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               /* update data to this threads sub-trunk */</span><br><span style="color: hsl(120, 100%, 40%);">+           w.x.rq.trunk = thread_info->this_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+            w.x.rq.pdatap->cfg = thread_info->this_cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           struct msgb *ret = thread_handle_mgcp_message(&w, thread_info);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!ret)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ssize_t srv = sendto(returnfd, ret->l2h, msgb_l2len(ret), 0, &w.x.addr.u.sa, sizeof(w.x.addr));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_free(ret);</span><br><span style="color: hsl(120, 100%, 40%);">+               memset(&w, 0, sizeof(w));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *split_per_thead(void *info)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  char thrdname[16];</span><br><span style="color: hsl(120, 100%, 40%);">+    struct per_thread_info *thread_info = (struct per_thread_info *)info;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_trunk *parent_trunk = thread_info->parent_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+       int this_thread_number = thread_info->tid;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       //top_ctx = talloc_named_const(NULL, 0, "top_thread_ctx");</span><br><span style="color: hsl(120, 100%, 40%);">+  /* NO msgb_alloc with default context allowed! TLS is slow! */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      thread_info->this_cfg = talloc_zero(top_ctx, struct mgcp_config);</span><br><span style="color: hsl(120, 100%, 40%);">+  thread_info->this_trunk = talloc_zero(top_ctx, struct mgcp_trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* just copy everything */</span><br><span style="color: hsl(120, 100%, 40%);">+    *thread_info->this_trunk = *parent_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+  *thread_info->this_cfg = *parent_trunk->cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+    thread_info->this_trunk->cfg = thread_info->this_cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      struct mgcp_trunk *t = thread_info->this_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  get_ep_num_per_thread(parent_trunk, this_thread_number, &t->v.vty_number_endpoints,</span><br><span style="color: hsl(120, 100%, 40%);">+                          &t->number_endpoints_offset);</span><br><span style="color: hsl(120, 100%, 40%);">+    thread_info->eps_free = t->v.vty_number_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* illegal copied members: */</span><br><span style="color: hsl(120, 100%, 40%);">+ t->keepalive_timer = (struct osmo_timer_list){ 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+        t->endpoints = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  t->number_endpoints = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   t->this_thread_info = info;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* keep our rate & stats counters</span><br><span style="color: hsl(120, 100%, 40%);">+ t->ratectr = (struct mgcp_ratectr_trunk){ 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+     t->stats = (struct mgcp_stat_trunk){ 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (parent_trunk->use_threads) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* give this thread a useful name */</span><br><span style="color: hsl(120, 100%, 40%);">+          snprintf(thrdname, 16, "%s_%d_%d", parent_trunk->trunk_type == MGCP_TRUNK_VIRTUAL ? "v" : "e1",</span><br><span style="color: hsl(120, 100%, 40%);">+                       t->trunk_nr, this_thread_number);</span><br><span style="color: hsl(120, 100%, 40%);">+         prctl(PR_SET_NAME, thrdname, NULL, NULL, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_cpu_sched_vty_apply_localthread();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_ctx_init(thrdname);</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_init_logging2(OTC_GLOBAL, &log_info);</span><br><span style="color: hsl(120, 100%, 40%);">+                log_enable_multithread();</span><br><span style="color: hsl(120, 100%, 40%);">+             //msgb_talloc_ctx_init(OTC_GLOBAL,0); nooo is global!</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_select_init();</span><br><span style="color: hsl(120, 100%, 40%);">+           //osmo_stats_init(OTC_GLOBAL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              /* this trunks rate ctrs are actually the parent trunks counters and not part of this ratectr!</span><br><span style="color: hsl(120, 100%, 40%);">+                this is only used for dynamically allocated per-conn ratecounters */</span><br><span style="color: hsl(120, 100%, 40%);">+          rate_ctr_init(OTC_GLOBAL);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mgcp_trunk_set_keepalive(t, t->keepalive_interval);</span><br><span style="color: hsl(120, 100%, 40%);">+        //mgcp_ratectr_trunk_alloc(t);</span><br><span style="color: hsl(120, 100%, 40%);">+        //mgcp_stat_trunk_alloc(t);</span><br><span style="color: hsl(120, 100%, 40%);">+   mgcp_trunk_equip(t);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!parent_trunk->use_threads)</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* fix wrong total count due to mgcp_trunk_equip */</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stat_item_set(osmo_stat_item_group_get_item(t->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL),</span><br><span style="color: hsl(120, 100%, 40%);">+                        parent_trunk->number_endpoints);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fd chan_mgcp_fd;</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fd_setup(&chan_mgcp_fd, get_a_rdfd(&thread_info->chan_mgcp), OSMO_FD_READ, &thread_cb, info, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fd_register(&chan_mgcp_fd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fd chan_cfg_fd;</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_fd_setup(&chan_cfg_fd, get_a_rdfd(&thread_info->chan_cfg), OSMO_FD_READ, &thread_cfg_cb, info, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_fd_register(&chan_cfg_fd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* main loop */</span><br><span style="color: hsl(120, 100%, 40%);">+       while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_select_main_ctx(0);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   //FIXME: shutdown</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void split_trunks_into_threads(struct mgcp_config *cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct mgcp_trunk *trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   prctl(PR_SET_NAME, "main", NULL, NULL, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry (trunk, &cfg->trunks, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            int num_threads = cfg->num_threads_for_virttrunk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* e1 has no threads! */</span><br><span style="color: hsl(120, 100%, 40%);">+              if (trunk->trunk_type == MGCP_TRUNK_E1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  num_threads = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                      trunk->use_threads = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           trunk->num_threads = num_threads;</span><br><span style="color: hsl(120, 100%, 40%);">+          trunk->thread_info = talloc_zero_array(cfg, struct per_thread_info, num_threads);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                for (int i = 0; i < num_threads; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    trunk->thread_info[i].parent_trunk = trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+                        trunk->thread_info[i].tid = i;</span><br><span style="color: hsl(120, 100%, 40%);">+                     trunk->thread_info[i].chan_mgcp = chan_init(10, sizeof(struct to_trunkthread_mgcp_msg));</span><br><span style="color: hsl(120, 100%, 40%);">+                   trunk->thread_info[i].chan_cfg =</span><br><span style="color: hsl(120, 100%, 40%);">+                           chan_init_ex(2, sizeof(struct to_trunkthread_cfg_msg), false, true, true, true);</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* currently unused</span><br><span style="color: hsl(120, 100%, 40%);">+                   osmo_fd_setup(&trunk->thread_info[i].master_evfd, get_b_rdfd(&trunk->thread_info[i].qc), OSMO_FD_READ, &master_cb, &trunk->thread_info[i], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_fd_register(&trunk->thread_info[i].master_evfd);</span><br><span style="color: hsl(120, 100%, 40%);">+*/</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (trunk->use_threads)</span><br><span style="color: hsl(120, 100%, 40%);">+                            pthread_create(&trunk->thread_info[i].thr, 0, split_per_thead, &trunk->thread_info[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+                 else</span><br><span style="color: hsl(120, 100%, 40%);">+                          split_per_thead(&trunk->thread_info[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_threads_vty.c b/src/libosmo-mgcp/mgcp_threads_vty.c</span><br><span>new file mode 100644</span><br><span>index 0000000..59bce2f</span><br><span>--- /dev/null</span><br><span>+++ b/src/libosmo-mgcp/mgcp_threads_vty.c</span><br><span>@@ -0,0 +1,189 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Eric Wild</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/sockaddr_str.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_endp.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct mgcp_rtp_state *state = &conn->state;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct mgcp_rtp_end *end = &conn->end;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_rtp_codec *codec = end->codec;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct rate_ctr *tx_packets, *tx_bytes;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct rate_ctr *rx_packets, *rx_bytes;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct rate_ctr *dropped_packets;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);</span><br><span style="color: hsl(120, 100%, 40%);">+     tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);</span><br><span style="color: hsl(120, 100%, 40%);">+        rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);</span><br><span style="color: hsl(120, 100%, 40%);">+     rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);</span><br><span style="color: hsl(120, 100%, 40%);">+        dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty,</span><br><span style="color: hsl(120, 100%, 40%);">+          "   Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"</span><br><span style="color: hsl(120, 100%, 40%);">+              "   Packets Received: %" PRIu64 " (%" PRIu64 " bytes total)%s"</span><br><span style="color: hsl(120, 100%, 40%);">+          "   Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+              "   Dropped Packets: %" PRIu64 "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+               "   Payload Type: %d Rate: %u Channels: %d %s"</span><br><span style="color: hsl(120, 100%, 40%);">+              "   Frame Duration: %u Frame Denominator: %u%s"</span><br><span style="color: hsl(120, 100%, 40%);">+             "   FPP: %d Packet Duration: %u%s"</span><br><span style="color: hsl(120, 100%, 40%);">+          "   FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"</span><br><span style="color: hsl(120, 100%, 40%);">+           "   Output-Enabled: %d Force-PTIME: %d%s",</span><br><span style="color: hsl(120, 100%, 40%);">+          tx_packets->current, tx_bytes->current, VTY_NEWLINE, rx_packets->current, rx_bytes->current,</span><br><span style="color: hsl(120, 100%, 40%);">+              VTY_NEWLINE, state->in_stream.err_ts_ctr->current, state->out_stream.err_ts_ctr->current, VTY_NEWLINE,</span><br><span style="color: hsl(120, 100%, 40%);">+            dropped_packets->current, VTY_NEWLINE, codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,</span><br><span style="color: hsl(120, 100%, 40%);">+            codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE, end->frames_per_packet,</span><br><span style="color: hsl(120, 100%, 40%);">+           end->packet_duration_ms, VTY_NEWLINE, end->fmtp_extra, codec->audio_name, codec->subtype_name,</span><br><span style="color: hsl(120, 100%, 40%);">+            VTY_NEWLINE, end->output_enabled, end->force_output_ptime, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, unsigned int trunk_nr, enum mgcp_trunk_type trunk_type,</span><br><span style="color: hsl(120, 100%, 40%);">+             int show_stats)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     vty_out(vty, "%s trunk %u endpoint %s:%s", trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr,</span><br><span style="color: hsl(120, 100%, 40%);">+               endp->name, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+  vty_out(vty, "   Availability: %s%s", mgcp_endp_avail(endp) ? "available" : "not in service", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (llist_empty(&endp->conns)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out(vty, "   No active connections%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry (conn, &endp->conns, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "   CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         if (show_stats) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (endp->trunk->cfg->conn_timeout) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                struct timeval remaining;</span><br><span style="color: hsl(120, 100%, 40%);">+                             osmo_timer_remaining(&conn->watchdog, NULL, &remaining);</span><br><span style="color: hsl(120, 100%, 40%);">+                           vty_out(vty, "   Currently remaining timeout (seconds): %d.%06d%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                                 (int)remaining.tv_sec, (int)remaining.tv_usec, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* FIXME: Also add verbosity for other</span><br><span style="color: hsl(120, 100%, 40%);">+                         * connection types (E1) as soon as</span><br><span style="color: hsl(120, 100%, 40%);">+                    * the implementation is available */</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (conn->type == MGCP_CONN_TYPE_RTP) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            dump_rtp_end(vty, &conn->u.rtp);</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void handle_vty_cmd(struct per_thread_info *thread_info, struct to_trunkthread_cfg_msg *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_trunk *t = thread_info->this_trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct vty *vty = m->vty;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (m->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case IS_VTYMSG: {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct vtymsg *w = &m->v;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* all our eps */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!strlen(w->epname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  for (int i = 0; i < t->number_endpoints; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         struct mgcp_endpoint *ep = t->endpoints[i];</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (!w->active_only || !llist_empty(&ep->conns))</span><br><span style="color: hsl(120, 100%, 40%);">+                                    dump_endpoint(m->vty, ep, t->trunk_nr, t->trunk_type, w->show_stats);</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      struct mgcp_endpoint *ep = mgcp_endp_find_specific(w->epname, t);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (ep && (!w->active_only || !llist_empty(&ep->conns)))</span><br><span style="color: hsl(120, 100%, 40%);">+                            dump_endpoint(m->vty, ep, t->trunk_nr, t->trunk_type, w->show_stats);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     case IS_FREEEPMSG: {</span><br><span style="color: hsl(120, 100%, 40%);">+          mgcp_endp_release(mgcp_endp_find_specific(m->f.endp, t));</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     case IS_TAPMSG: {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct tapmsg *w = &m->t;</span><br><span style="color: hsl(120, 100%, 40%);">+              struct mgcp_endpoint *endp = mgcp_endp_find_specific(w->epname, t);</span><br><span style="color: hsl(120, 100%, 40%);">+                struct mgcp_conn_rtp *conn = mgcp_conn_get_rtp(endp, w->connid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  vty_out(vty, "Conn ID %s is invalid.%s", w->connid, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                        goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           struct mgcp_rtp_tap *tap = w->direction_is_in ? &conn->tap_in : &conn->tap_out;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            memset(&tap->forward, 0, sizeof(tap->forward));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           tap->forward.u.sa.sa_family = osmo_ip_str_type(w->addr);</span><br><span style="color: hsl(120, 100%, 40%);">+                switch (tap->forward.u.sa.sa_family) {</span><br><span style="color: hsl(120, 100%, 40%);">+             case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (inet_pton(AF_INET, w->addr, &tap->forward.u.sin.sin_addr) != 1)</span><br><span style="color: hsl(120, 100%, 40%);">+                         goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+                     tap->forward.u.sin.sin_port = w->destport;</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (inet_pton(AF_INET6, w->addr, &tap->forward.u.sin6.sin6_addr) != 1)</span><br><span style="color: hsl(120, 100%, 40%);">+                              goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+                     tap->forward.u.sin6.sin6_port = w->destport;</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             tap->enabled = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     case IS_LOOPMSG: {</span><br><span style="color: hsl(120, 100%, 40%);">+            struct loopmsg *w = &m->l;</span><br><span style="color: hsl(120, 100%, 40%);">+             struct mgcp_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+               struct mgcp_endpoint *endp = mgcp_endp_find_specific(w->epname, t);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry (conn, &endp->conns, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (conn->type == MGCP_CONN_TYPE_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Handle it like a MDCX, switch on SSRC patching if enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+                               mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);</span><br><span style="color: hsl(120, 100%, 40%);">+                        else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                /* FIXME: Introduce support for other connection (E1)</span><br><span style="color: hsl(120, 100%, 40%);">+                  * types when implementation is available */</span><br><span style="color: hsl(120, 100%, 40%);">+                          vty_out(vty,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "%%Can't enable SSRC patching,"</span><br><span style="color: hsl(120, 100%, 40%);">+                                 "connection %s is not an RTP connection.%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        mgcp_conn_dump(conn), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (w->enable_loop)</span><br><span style="color: hsl(120, 100%, 40%);">+                                conn->mode = MGCP_CONN_LOOPBACK;</span><br><span style="color: hsl(120, 100%, 40%);">+                   else</span><br><span style="color: hsl(120, 100%, 40%);">+                          conn->mode = conn->mode_orig;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   vty_flush(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+       /* lazily ack by sending back the message */</span><br><span style="color: hsl(120, 100%, 40%);">+  while (!spsc_push(thread_info->chan_cfg.b, m))</span><br><span style="color: hsl(120, 100%, 40%);">+             ;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c</span><br><span>index f41e5a5..4ac0454 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_trunk.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_trunk.c</span><br><span>@@ -69,6 +69,7 @@</span><br><span>    trunk->audio_send_name = 1;</span><br><span>       trunk->v.vty_number_endpoints = 512;</span><br><span>      trunk->omit_rtcp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      trunk->number_endpoints_offset = 0;</span><br><span> </span><br><span>   mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);</span><br><span> </span><br><span>@@ -102,13 +103,13 @@</span><br><span>    case MGCP_TRUNK_VIRTUAL:</span><br><span>             /* Due to historical reasons the endpoints on the virtual</span><br><span>             * trunk start counting at 1. */</span><br><span style="color: hsl(0, 100%, 40%);">-                first_endpoint_nr = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                first_endpoint_nr = trunk->number_endpoints_offset + 1;</span><br><span>           number_endpoints = trunk->v.vty_number_endpoints;</span><br><span>                 break;</span><br><span>       case MGCP_TRUNK_E1:</span><br><span>          /* The first timeslot on an E1 line is reserved for framing</span><br><span>           * and alignment and can not be used for audio transport */</span><br><span style="color: hsl(0, 100%, 40%);">-             first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;</span><br><span style="color: hsl(120, 100%, 40%);">+                first_endpoint_nr = trunk->number_endpoints_offset + 1 * MGCP_ENDP_E1_SUBSLOTS;</span><br><span>           number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;</span><br><span>            break;</span><br><span>       default:</span><br><span>diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c</span><br><span>index a05733f..062442c 100644</span><br><span>--- a/src/libosmo-mgcp/mgcp_vty.c</span><br><span>+++ b/src/libosmo-mgcp/mgcp_vty.c</span><br><span>@@ -32,7 +32,9 @@</span><br><span> #include <osmocom/mgcp/mgcp_conn.h></span><br><span> #include <osmocom/mgcp/mgcp_endp.h></span><br><span> #include <osmocom/mgcp/mgcp_trunk.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span> #include <string.h></span><br><span> #include <inttypes.h></span><br><span> #include <limits.h></span><br><span>@@ -120,6 +122,8 @@</span><br><span>             trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);</span><br><span>    vty_out(vty, " number endpoints %u%s",</span><br><span>             trunk->v.vty_number_endpoints, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+       vty_out(vty, " number threads %u%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                g_cfg->num_threads_for_virttrunk, VTY_NEWLINE);</span><br><span>   vty_out(vty, " %sallow-transcoding%s",</span><br><span>             trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);</span><br><span>       if (strlen(g_cfg->call_agent_addr))</span><br><span>@@ -129,7 +133,7 @@</span><br><span>                 vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,</span><br><span>                       VTY_NEWLINE);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       switch (g_cfg->osmux) {</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (g_cfg->global_osmux_options.osmux) {</span><br><span>      case OSMUX_USAGE_ON:</span><br><span>                 vty_out(vty, " osmux on%s", VTY_NEWLINE);</span><br><span>          break;</span><br><span>@@ -141,17 +145,17 @@</span><br><span>               vty_out(vty, " osmux off%s", VTY_NEWLINE);</span><br><span>                 break;</span><br><span>       }</span><br><span style="color: hsl(0, 100%, 40%);">-       if (g_cfg->osmux) {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (g_cfg->global_osmux_options.osmux) {</span><br><span>          vty_out(vty, " osmux bind-ip %s%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                   g_cfg->osmux_addr, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                   g_cfg->global_osmux_options.osmux_addr, VTY_NEWLINE);</span><br><span>             vty_out(vty, " osmux batch-factor %d%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                      g_cfg->osmux_batch, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                  g_cfg->global_osmux_options.osmux_batch, VTY_NEWLINE);</span><br><span>            vty_out(vty, " osmux batch-size %u%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                        g_cfg->osmux_batch_size, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                     g_cfg->global_osmux_options.osmux_batch_size, VTY_NEWLINE);</span><br><span>               vty_out(vty, " osmux port %u%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                      g_cfg->osmux_port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                   g_cfg->global_osmux_options.osmux_port, VTY_NEWLINE);</span><br><span>             vty_out(vty, " osmux dummy %s%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                     g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                       g_cfg->global_osmux_options.osmux_dummy ? "on" : "off", VTY_NEWLINE);</span><br><span>         }</span><br><span> </span><br><span>        if (g_cfg->conn_timeout)</span><br><span>@@ -160,81 +164,6 @@</span><br><span>   return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct mgcp_rtp_state *state = &conn->state;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct mgcp_rtp_end *end = &conn->end;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct mgcp_rtp_codec *codec = end->codec;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct rate_ctr *tx_packets, *tx_bytes;</span><br><span style="color: hsl(0, 100%, 40%);">- struct rate_ctr *rx_packets, *rx_bytes;</span><br><span style="color: hsl(0, 100%, 40%);">- struct rate_ctr *dropped_packets;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);</span><br><span style="color: hsl(0, 100%, 40%);">-       tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);</span><br><span style="color: hsl(0, 100%, 40%);">-  rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);</span><br><span style="color: hsl(0, 100%, 40%);">-       rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);</span><br><span style="color: hsl(0, 100%, 40%);">-  dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     vty_out(vty,</span><br><span style="color: hsl(0, 100%, 40%);">-            "   Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"</span><br><span style="color: hsl(0, 100%, 40%);">-                "   Packets Received: %" PRIu64 " (%" PRIu64 " bytes total)%s"</span><br><span style="color: hsl(0, 100%, 40%);">-            "   Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"</span><br><span style="color: hsl(0, 100%, 40%);">-                "   Dropped Packets: %" PRIu64 "%s"</span><br><span style="color: hsl(0, 100%, 40%);">-         "   Payload Type: %d Rate: %u Channels: %d %s"</span><br><span style="color: hsl(0, 100%, 40%);">-                "   Frame Duration: %u Frame Denominator: %u%s"</span><br><span style="color: hsl(0, 100%, 40%);">-               "   FPP: %d Packet Duration: %u%s"</span><br><span style="color: hsl(0, 100%, 40%);">-            "   FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"</span><br><span style="color: hsl(0, 100%, 40%);">-             "   Output-Enabled: %d Force-PTIME: %d%s",</span><br><span style="color: hsl(0, 100%, 40%);">-            tx_packets->current, tx_bytes->current, VTY_NEWLINE,</span><br><span style="color: hsl(0, 100%, 40%);">-              rx_packets->current, rx_bytes->current, VTY_NEWLINE,</span><br><span style="color: hsl(0, 100%, 40%);">-              state->in_stream.err_ts_ctr->current,</span><br><span style="color: hsl(0, 100%, 40%);">-             state->out_stream.err_ts_ctr->current,</span><br><span style="color: hsl(0, 100%, 40%);">-            VTY_NEWLINE,</span><br><span style="color: hsl(0, 100%, 40%);">-            dropped_packets->current, VTY_NEWLINE,</span><br><span style="color: hsl(0, 100%, 40%);">-               codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,</span><br><span style="color: hsl(0, 100%, 40%);">-                codec->frame_duration_num, codec->frame_duration_den,</span><br><span style="color: hsl(0, 100%, 40%);">-             VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,</span><br><span style="color: hsl(0, 100%, 40%);">-             VTY_NEWLINE, end->fmtp_extra, codec->audio_name,</span><br><span style="color: hsl(0, 100%, 40%);">-          codec->subtype_name, VTY_NEWLINE, end->output_enabled,</span><br><span style="color: hsl(0, 100%, 40%);">-            end->force_output_ptime, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,</span><br><span style="color: hsl(0, 100%, 40%);">-                          unsigned int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct mgcp_conn *conn;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- vty_out(vty, "%s trunk %u endpoint %s:%s",</span><br><span style="color: hsl(0, 100%, 40%);">-            trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, endp->name, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">- vty_out(vty, "   Availability: %s%s",</span><br><span style="color: hsl(0, 100%, 40%);">-         mgcp_endp_avail(endp) ? "available" : "not in service", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (llist_empty(&endp->conns)) {</span><br><span style="color: hsl(0, 100%, 40%);">-         vty_out(vty, "   No active connections%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-              return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       llist_for_each_entry(conn, &endp->conns, entry) {</span><br><span style="color: hsl(0, 100%, 40%);">-                vty_out(vty, "   CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-             if (show_stats) {</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (endp->trunk->cfg->conn_timeout) {</span><br><span style="color: hsl(0, 100%, 40%);">-                          struct timeval remaining;</span><br><span style="color: hsl(0, 100%, 40%);">-                               osmo_timer_remaining(&conn->watchdog, NULL, &remaining);</span><br><span style="color: hsl(0, 100%, 40%);">-                             vty_out(vty, "   Currently remaining timeout (seconds): %d.%06d%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                                   (int)remaining.tv_sec, (int)remaining.tv_usec, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-                    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       /* FIXME: Also add verbosity for other</span><br><span style="color: hsl(0, 100%, 40%);">-                   * connection types (E1) as soon as</span><br><span style="color: hsl(0, 100%, 40%);">-                      * the implementation is available */</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (conn->type == MGCP_CONN_TYPE_RTP) {</span><br><span style="color: hsl(0, 100%, 40%);">-                              dump_rtp_end(vty, &conn->u.rtp);</span><br><span style="color: hsl(0, 100%, 40%);">-                 }</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static void dump_ratectr_global(struct vty *vty, struct mgcp_ratectr_global *ratectr)</span><br><span> {</span><br><span>     vty_out(vty, "%s", VTY_NEWLINE);</span><br><span>@@ -303,9 +232,6 @@</span><br><span> </span><br><span> static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats, int active_only)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- int i;</span><br><span style="color: hsl(0, 100%, 40%);">-  int active_count = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        vty_out(vty, "%s trunk %d with %d endpoints:%s",</span><br><span>           trunk->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",</span><br><span>           trunk->trunk_nr, trunk->number_endpoints, VTY_NEWLINE);</span><br><span>@@ -315,21 +241,13 @@</span><br><span>                return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   for (i = 0; i < trunk->number_endpoints; ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">-           struct mgcp_endpoint *endp = trunk->endpoints[i];</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!active_only || !llist_empty(&endp->conns)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,</span><br><span style="color: hsl(0, 100%, 40%);">-                                    show_stats);</span><br><span style="color: hsl(0, 100%, 40%);">-                      if (i < trunk->number_endpoints - 1)</span><br><span style="color: hsl(0, 100%, 40%);">-                              vty_out(vty, "%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-               if (!llist_empty(&endp->conns))</span><br><span style="color: hsl(0, 100%, 40%);">-                  active_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+       vty_out(vty, "%s endpoints: %s", active_only ? "ACTIVE" : "ALL", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+  for (int i = 0; i < trunk->num_threads; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          thread_dispatch_ep_dump_msg(trunk, i, vty, 0, show_stats, active_only);</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out(vty, "%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            vty_flush(vty);</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (active_count == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-          vty_out(vty, "No endpoints in use.%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       if (show_stats)</span><br><span>              dump_ratectr_trunk(vty, trunk);</span><br><span> }</span><br><span>@@ -342,7 +260,7 @@</span><br><span>   llist_for_each_entry(trunk, &g_cfg->trunks, entry)</span><br><span>            dump_trunk(vty, trunk, show_stats, active_only);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if (g_cfg->osmux)</span><br><span style="color: hsl(120, 100%, 40%);">+  if (g_cfg->global_osmux_options.osmux)</span><br><span>            vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),</span><br><span>                  VTY_NEWLINE);</span><br><span> </span><br><span>@@ -377,25 +295,28 @@</span><br><span> dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname)</span><br><span> {</span><br><span>  struct mgcp_endpoint *endp;</span><br><span style="color: hsl(120, 100%, 40%);">+   int threadnum;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      if (trunk) {</span><br><span style="color: hsl(0, 100%, 40%);">-            /* If a trunk is given, search on that specific trunk only */</span><br><span style="color: hsl(0, 100%, 40%);">-           endp = mgcp_endp_by_name_trunk(NULL, epname, trunk);</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!endp) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    vty_out(vty, "endpoint %s not configured on trunk %u%s", epname, trunk->trunk_nr, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-                    return;</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!trunk) {</span><br><span>                /* If no trunk is given, search on all possible trunks */</span><br><span>            endp = mgcp_endp_by_name(NULL, epname, g_cfg);</span><br><span>               if (!endp) {</span><br><span>                         vty_out(vty, "endpoint %s not configured%s", epname, VTY_NEWLINE);</span><br><span>                         return;</span><br><span>              }</span><br><span style="color: hsl(120, 100%, 40%);">+             trunk = endp->trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* If a trunk is given, search on that specific trunk only */</span><br><span style="color: hsl(120, 100%, 40%);">+         endp = mgcp_endp_by_name_trunk(NULL, epname, trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!endp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  vty_out(vty, "endpoint %s not configured on trunk %u%s", epname,</span><br><span style="color: hsl(120, 100%, 40%);">+                     trunk->trunk_nr, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   trunk = endp->trunk;</span><br><span style="color: hsl(0, 100%, 40%);">- dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type, true);</span><br><span style="color: hsl(120, 100%, 40%);">+     threadnum = get_trunk_thread_for_ep_name(epname, trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+      thread_dispatch_ep_dump_msg(trunk, threadnum, vty, epname, true, false);</span><br><span> }</span><br><span> </span><br><span> DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,</span><br><span>@@ -424,6 +345,8 @@</span><br><span>                 return CMD_WARNING;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* single e1 thread trunk */</span><br><span style="color: hsl(120, 100%, 40%);">+  trunk = trunk->thread_info[0].this_trunk;</span><br><span>         dump_mgcp_endpoint(vty, trunk, argv[1]);</span><br><span>     return CMD_SUCCESS;</span><br><span> }</span><br><span>@@ -444,6 +367,7 @@</span><br><span>             "IPv6 Address to use in SDP record\n")</span><br><span> {</span><br><span>  osmo_strlcpy(g_cfg->local_ip, argv[0], sizeof(g_cfg->local_ip));</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -456,6 +380,7 @@</span><br><span>       "IPv6 Address to bind to\n")</span><br><span> {</span><br><span>      osmo_strlcpy(g_cfg->source_addr, argv[0], sizeof(g_cfg->source_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -466,6 +391,7 @@</span><br><span> {</span><br><span>      unsigned int port = atoi(argv[0]);</span><br><span>   g_cfg->source_port = port;</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -517,7 +443,7 @@</span><br><span>   g_cfg->net_ports.range_start = start;</span><br><span>     g_cfg->net_ports.range_end = end;</span><br><span>         g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,</span><br><span>@@ -534,6 +460,7 @@</span><br><span>        "IPv4 Address to bind to\n")</span><br><span> {</span><br><span>    osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, argv[0], sizeof(g_cfg->net_ports.bind_addr_v4));</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip,</span><br><span>@@ -549,6 +476,7 @@</span><br><span>           "Address to bind to\n")</span><br><span> {</span><br><span>         osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, "", sizeof(g_cfg->net_ports.bind_addr_v4));</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip,</span><br><span>@@ -565,6 +493,7 @@</span><br><span>        "IPv6 Address to bind to\n")</span><br><span> {</span><br><span>    osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, argv[0], sizeof(g_cfg->net_ports.bind_addr_v6));</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -576,6 +505,7 @@</span><br><span>         "Address to bind to\n")</span><br><span> {</span><br><span>         osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, "", sizeof(g_cfg->net_ports.bind_addr_v6));</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -586,6 +516,7 @@</span><br><span>         RTP_STR "automatic rtp bind ip selection\n")</span><br><span> {</span><br><span>    g_cfg->net_ports.bind_addr_probe = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -596,6 +527,7 @@</span><br><span>         NO_STR RTP_STR "no automatic rtp bind ip selection\n")</span><br><span> {</span><br><span>  g_cfg->net_ports.bind_addr_probe = false;</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -608,6 +540,7 @@</span><br><span> {</span><br><span>      int dscp = atoi(argv[0]);</span><br><span>    g_cfg->endp_dscp = dscp;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -620,6 +553,7 @@</span><br><span> {</span><br><span>      int prio = atoi(argv[0]);</span><br><span>    g_cfg->endp_priority = prio;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -632,6 +566,7 @@</span><br><span>         "The required ptime (packet duration) in ms\n" "10 ms\n20 ms\n40 ms\n")</span><br><span> {</span><br><span>       g_cfg->force_ptime = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -641,6 +576,7 @@</span><br><span>         "no rtp force-ptime", NO_STR RTP_STR FORCE_PTIME_STR)</span><br><span> {</span><br><span>   g_cfg->force_ptime = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -659,6 +595,7 @@</span><br><span> </span><br><span>       osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);</span><br><span>     talloc_free(txt);</span><br><span style="color: hsl(120, 100%, 40%);">+     send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -670,6 +607,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->no_audio_transcoding = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -681,6 +619,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->no_audio_transcoding = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -720,6 +659,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->audio_send_ptime = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -732,6 +672,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->audio_send_ptime = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -744,6 +685,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->audio_send_name = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -756,6 +698,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->audio_send_name = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -777,6 +720,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->force_realloc = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -790,6 +734,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->rtp_accept_all = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+     send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -811,6 +756,7 @@</span><br><span> {</span><br><span>      struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         trunk->omit_rtcp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -823,6 +769,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->omit_rtcp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -834,6 +781,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->force_constant_ssrc = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -845,6 +793,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->force_constant_ssrc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -856,6 +805,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->force_aligned_timing = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -867,6 +817,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->force_aligned_timing = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -878,6 +829,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->rfc5993_hr_convert = true;</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -889,6 +841,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  trunk->rfc5993_hr_convert = false;</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -902,6 +855,7 @@</span><br><span>   trunk->force_constant_ssrc = 0;</span><br><span>   trunk->force_aligned_timing = 0;</span><br><span>  trunk->rfc5993_hr_convert = false;</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -914,6 +868,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  mgcp_trunk_set_keepalive(trunk, atoi(argv[0]));</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -926,6 +881,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -937,6 +893,7 @@</span><br><span>   struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>         OSMO_ASSERT(trunk);</span><br><span>  mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_NEVER);</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -949,6 +906,7 @@</span><br><span>       "IPv6 Address of the call agent\n")</span><br><span> {</span><br><span>       osmo_strlcpy(g_cfg->call_agent_addr, argv[0], sizeof(g_cfg->call_agent_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1052,6 +1010,7 @@</span><br><span> </span><br><span>     osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);</span><br><span>     talloc_free(txt);</span><br><span style="color: hsl(120, 100%, 40%);">+     send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1097,6 +1056,7 @@</span><br><span>         struct mgcp_trunk *trunk = vty->index;</span><br><span>    OSMO_ASSERT(trunk);</span><br><span>  trunk->force_realloc = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1110,6 +1070,7 @@</span><br><span>         struct mgcp_trunk *trunk = vty->index;</span><br><span>    OSMO_ASSERT(trunk);</span><br><span>  trunk->rtp_accept_all = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+     send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1121,6 +1082,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->audio_send_ptime = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1132,6 +1094,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->audio_send_ptime = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1143,6 +1106,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->audio_send_name = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1154,6 +1118,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->audio_send_name = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1164,6 +1129,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->omit_rtcp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1174,6 +1140,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->omit_rtcp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1184,6 +1151,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->force_constant_ssrc = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1194,6 +1162,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->force_constant_ssrc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1204,6 +1173,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->force_aligned_timing = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1214,6 +1184,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->force_aligned_timing = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1224,6 +1195,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->rfc5993_hr_convert = true;</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1234,6 +1206,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->rfc5993_hr_convert = false;</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1246,6 +1219,7 @@</span><br><span>         trunk->force_constant_ssrc = 0;</span><br><span>   trunk->force_aligned_timing = 0;</span><br><span>  trunk->rfc5993_hr_convert = false;</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1257,6 +1231,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    mgcp_trunk_set_keepalive(trunk, atoi(argv[0]));</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1268,6 +1243,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);</span><br><span style="color: hsl(120, 100%, 40%);">+ send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1278,6 +1254,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    mgcp_trunk_set_keepalive(trunk, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1288,6 +1265,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->no_audio_transcoding = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1298,6 +1276,7 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk = vty->index;</span><br><span>    trunk->no_audio_transcoding = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1311,6 +1290,7 @@</span><br><span>         struct mgcp_trunk *trunk = vty->index;</span><br><span>    int line_nr = atoi(argv[0]);</span><br><span>         trunk->e1.vty_line_nr = line_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_trunk_update_msg(trunk);</span><br><span>      return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1323,7 +1303,8 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk;</span><br><span>    struct mgcp_endpoint *endp;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct mgcp_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+       int threadnum;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct to_trunkthread_cfg_msg m = {0};</span><br><span> </span><br><span>   trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_E1, atoi(argv[0]));</span><br><span>      if (!trunk) {</span><br><span>@@ -1346,24 +1327,13 @@</span><br><span>      }</span><br><span> </span><br><span>        endp = trunk->endpoints[endp_no];</span><br><span style="color: hsl(0, 100%, 40%);">-    int loop = atoi(argv[2]);</span><br><span style="color: hsl(0, 100%, 40%);">-       llist_for_each_entry(conn, &endp->conns, entry) {</span><br><span style="color: hsl(0, 100%, 40%);">-                if (conn->type == MGCP_CONN_TYPE_RTP)</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* Handle it like a MDCX, switch on SSRC patching if enabled */</span><br><span style="color: hsl(0, 100%, 40%);">-                 mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);</span><br><span style="color: hsl(0, 100%, 40%);">-          else {</span><br><span style="color: hsl(0, 100%, 40%);">-                  /* FIXME: Introduce support for other connection (E1)</span><br><span style="color: hsl(0, 100%, 40%);">-                    * types when implementation is available */</span><br><span style="color: hsl(0, 100%, 40%);">-                    vty_out(vty, "%%Can't enable SSRC patching,"</span><br><span style="color: hsl(0, 100%, 40%);">-                              "connection %s is not an RTP connection.%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                          mgcp_conn_dump(conn), VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (loop)</span><br><span style="color: hsl(0, 100%, 40%);">-                       conn->mode = MGCP_CONN_LOOPBACK;</span><br><span style="color: hsl(0, 100%, 40%);">-             else</span><br><span style="color: hsl(0, 100%, 40%);">-                    conn->mode = conn->mode_orig;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ threadnum = get_trunk_thread_for_ep_name(endp->name, trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+       m.l.enable_loop = atoi(argv[2]);;</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_strlcpy(m.l.epname, endp->name, sizeof(m.t.epname));</span><br><span style="color: hsl(120, 100%, 40%);">+  thread_dispatch_ep_msg(trunk, threadnum, vty, IS_LOOPMSG, &m);</span><br><span> </span><br><span>       return CMD_SUCCESS;</span><br><span> }</span><br><span>@@ -1380,11 +1350,10 @@</span><br><span>       "Destination IPv6 of the data\n"</span><br><span>       "Destination port\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct mgcp_rtp_tap *tap;</span><br><span>    struct mgcp_trunk *trunk;</span><br><span>    struct mgcp_endpoint *endp;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct mgcp_conn_rtp *conn;</span><br><span style="color: hsl(0, 100%, 40%);">-        const char *conn_id = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        int threadnum;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct to_trunkthread_cfg_msg m = {0};</span><br><span> </span><br><span>   trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_E1, atoi(argv[0]));</span><br><span>      if (!trunk) {</span><br><span>@@ -1408,41 +1377,26 @@</span><br><span> </span><br><span>  endp = trunk->endpoints[endp_no];</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        conn_id = argv[2];</span><br><span style="color: hsl(0, 100%, 40%);">-      conn = mgcp_conn_get_rtp(endp, conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!conn) {</span><br><span style="color: hsl(0, 100%, 40%);">-            vty_out(vty, "Conn ID %s is invalid.%s",</span><br><span style="color: hsl(0, 100%, 40%);">-                      conn_id, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-          return CMD_WARNING;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(120, 100%, 40%);">+/// v---</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       bool direction_is_in = false;</span><br><span>        if (strcmp(argv[3], "in") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-               tap = &conn->tap_in;</span><br><span style="color: hsl(120, 100%, 40%);">+           direction_is_in = true;</span><br><span>      else if (strcmp(argv[3], "out") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-         tap = &conn->tap_out;</span><br><span style="color: hsl(120, 100%, 40%);">+  {}</span><br><span>   else {</span><br><span>               vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);</span><br><span>               return CMD_WARNING;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   memset(&tap->forward, 0, sizeof(tap->forward));</span><br><span style="color: hsl(120, 100%, 40%);">+     threadnum = get_trunk_thread_for_ep_name(endp->name, trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+       m.t.direction_is_in = direction_is_in;</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_strlcpy(m.t.epname, endp->name, sizeof(m.t.epname));</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_strlcpy(m.t.connid, argv[2], sizeof(m.t.connid));</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_strlcpy(m.t.addr, argv[4], sizeof(m.t.addr));</span><br><span style="color: hsl(120, 100%, 40%);">+    m.t.destport = htons(atoi(argv[5]));</span><br><span style="color: hsl(120, 100%, 40%);">+  thread_dispatch_ep_msg(trunk, threadnum, vty, IS_TAPMSG, &m);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   tap->forward.u.sa.sa_family = osmo_ip_str_type(argv[4]);</span><br><span style="color: hsl(0, 100%, 40%);">-     switch (tap->forward.u.sa.sa_family) {</span><br><span style="color: hsl(0, 100%, 40%);">-       case AF_INET:</span><br><span style="color: hsl(0, 100%, 40%);">-           if (inet_pton(AF_INET, argv[4], &tap->forward.u.sin.sin_addr) != 1)</span><br><span style="color: hsl(0, 100%, 40%);">-                      return CMD_WARNING;</span><br><span style="color: hsl(0, 100%, 40%);">-             tap->forward.u.sin.sin_port = htons(atoi(argv[5]));</span><br><span style="color: hsl(0, 100%, 40%);">-          break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case AF_INET6:</span><br><span style="color: hsl(0, 100%, 40%);">-          if (inet_pton(AF_INET6, argv[4], &tap->forward.u.sin6.sin6_addr) != 1)</span><br><span style="color: hsl(0, 100%, 40%);">-                   return CMD_WARNING;</span><br><span style="color: hsl(0, 100%, 40%);">-             tap->forward.u.sin6.sin6_port = htons(atoi(argv[5]));</span><br><span style="color: hsl(0, 100%, 40%);">-                break;</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                return CMD_WARNING;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-       tap->enabled = 1;</span><br><span>         return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1452,6 +1406,8 @@</span><br><span> {</span><br><span>    struct mgcp_trunk *trunk;</span><br><span>    struct mgcp_endpoint *endp;</span><br><span style="color: hsl(120, 100%, 40%);">+   int threadnum;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct to_trunkthread_cfg_msg m = {0};</span><br><span> </span><br><span>   trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_E1, atoi(argv[0]));</span><br><span>      if (!trunk) {</span><br><span>@@ -1474,7 +1430,11 @@</span><br><span>       }</span><br><span> </span><br><span>        endp = trunk->endpoints[endp_no];</span><br><span style="color: hsl(0, 100%, 40%);">-    mgcp_endp_release(endp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    threadnum = get_trunk_thread_for_ep_name(endp->name, trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_strlcpy(m.f.endp, endp->name, sizeof(m.f.endp));</span><br><span style="color: hsl(120, 100%, 40%);">+      thread_dispatch_ep_msg(trunk, threadnum, vty, IS_FREEEPMSG, &m);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1539,13 +1499,14 @@</span><br><span>       OSMO_ASSERT(trunk);</span><br><span> </span><br><span>      if (strcmp(argv[0], "off") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-            g_cfg->osmux = OSMUX_USAGE_OFF;</span><br><span style="color: hsl(120, 100%, 40%);">+            g_cfg->global_osmux_options.osmux = OSMUX_USAGE_OFF;</span><br><span>              return CMD_SUCCESS;</span><br><span>  } else if (strcmp(argv[0], "on") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                g_cfg->osmux = OSMUX_USAGE_ON;</span><br><span style="color: hsl(120, 100%, 40%);">+             g_cfg->global_osmux_options.osmux = OSMUX_USAGE_ON;</span><br><span>       else if (strcmp(argv[0], "only") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                g_cfg->osmux = OSMUX_USAGE_ONLY;</span><br><span style="color: hsl(120, 100%, 40%);">+           g_cfg->global_osmux_options.osmux = OSMUX_USAGE_ONLY;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> </span><br><span> }</span><br><span>@@ -1557,7 +1518,8 @@</span><br><span>       "IPv4 Address to bind to\n"</span><br><span>       "IPv6 Address to bind to\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_strlcpy(g_cfg->global_osmux_options.osmux_addr, argv[0], sizeof(g_cfg->global_osmux_options.osmux_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1566,7 +1528,8 @@</span><br><span>       "osmux batch-factor <1-8>",</span><br><span>       OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  g_cfg->osmux_batch = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+        g_cfg->global_osmux_options.osmux_batch = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+   send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1575,7 +1538,8 @@</span><br><span>       "osmux batch-size <1-65535>",</span><br><span>       OSMUX_STR "batch size\n" "Batch size in bytes\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- g_cfg->osmux_batch_size = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+   g_cfg->global_osmux_options.osmux_batch_size = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+      send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1583,7 +1547,8 @@</span><br><span>       cfg_mgcp_osmux_port_cmd,</span><br><span>       "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       g_cfg->osmux_port = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ g_cfg->global_osmux_options.osmux_port = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1594,10 +1559,10 @@</span><br><span>       "Disable dummy padding\n")</span><br><span> {</span><br><span>    if (strcmp(argv[0], "on") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-               g_cfg->osmux_dummy = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+            g_cfg->global_osmux_options.osmux_dummy = 1;</span><br><span>      else if (strcmp(argv[0], "off") == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-         g_cfg->osmux_dummy = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+            g_cfg->global_osmux_options.osmux_dummy = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1608,6 +1573,7 @@</span><br><span>       "Qualified domain name expected in MGCP endpoint names, or '*' to accept any domain\n")</span><br><span> {</span><br><span>         osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));</span><br><span style="color: hsl(120, 100%, 40%);">+    send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1621,6 +1587,23 @@</span><br><span>       "Timeout value (sec.)\n")</span><br><span> {</span><br><span>      g_cfg->conn_timeout = strtoul(argv[0], NULL, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+  send_async_vty_cfg_update_msg(g_cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_mgcp_num_threads_for_virttrunk,</span><br><span style="color: hsl(120, 100%, 40%);">+      cfg_mgcp_num_threads_for_virttrunk_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "number threads <1-65534>",</span><br><span style="color: hsl(120, 100%, 40%);">+      "Sets the number of threads that will handle the configured endpoints,"</span><br><span style="color: hsl(120, 100%, 40%);">+          " the thread name is automatially set to <v|e1>_<trunknum>_<threadnum> starting with thread 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      "Number of threads\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(trunk);</span><br><span style="color: hsl(120, 100%, 40%);">+   int v = strtoul(argv[0], NULL, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+   g_cfg->num_threads_for_virttrunk = v;</span><br><span style="color: hsl(120, 100%, 40%);">+      trunk->use_threads = v ? true : false;</span><br><span style="color: hsl(120, 100%, 40%);">+     send_async_vty_cfg_update_msg(g_cfg);</span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>@@ -1694,6 +1677,7 @@</span><br><span>         install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);</span><br><span>  install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);</span><br><span>        install_element(MGCP_NODE, &cfg_mgcp_conn_timeout_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+   install_element(MGCP_NODE, &cfg_mgcp_num_threads_for_virttrunk_cmd);</span><br><span> </span><br><span>         install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);</span><br><span>         install_node(&trunk_node, config_write_trunk);</span><br><span>@@ -1734,9 +1718,10 @@</span><br><span>  int rc;</span><br><span>      struct mgcp_trunk *trunk;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   cfg->osmux_port = OSMUX_PORT;</span><br><span style="color: hsl(0, 100%, 40%);">-        cfg->osmux_batch = 4;</span><br><span style="color: hsl(0, 100%, 40%);">-        cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;</span><br><span style="color: hsl(120, 100%, 40%);">+   cfg->global_osmux_options.osmux_port = OSMUX_PORT;</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg->global_osmux_options.osmux_batch = 4;</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg->global_osmux_options.osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;</span><br><span style="color: hsl(120, 100%, 40%);">+      cfg->num_threads_for_virttrunk = 1;</span><br><span> </span><br><span>   g_cfg = cfg;</span><br><span>         rc = vty_read_config_file(config_file, NULL);</span><br><span>@@ -1761,5 +1746,6 @@</span><br><span>        }</span><br><span>    cfg->role = role;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      osmux_set_global_opt(&g_cfg->global_osmux_options);</span><br><span>   return 0;</span><br><span> }</span><br><span>diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c</span><br><span>index d12011c..cf7fad5 100644</span><br><span>--- a/src/osmo-mgw/mgw_main.c</span><br><span>+++ b/src/osmo-mgw/mgw_main.c</span><br><span>@@ -42,6 +42,7 @@</span><br><span> #include <osmocom/mgcp/mgcp_endp.h></span><br><span> #include <osmocom/mgcp/mgcp_trunk.h></span><br><span> #include <osmocom/mgcp/mgcp_ctrl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span> </span><br><span> #include <osmocom/core/application.h></span><br><span> #include <osmocom/core/msgb.h></span><br><span>@@ -77,8 +78,6 @@</span><br><span> /* FIXME: Make use of the rtp proxy code */</span><br><span> </span><br><span> static struct mgcp_config *cfg;</span><br><span style="color: hsl(0, 100%, 40%);">-static struct mgcp_trunk *reset_trunk;</span><br><span style="color: hsl(0, 100%, 40%);">-static int reset_endpoints = 0;</span><br><span> static int daemonize = 0;</span><br><span> </span><br><span> const char *osmomgw_copyright =</span><br><span>@@ -187,66 +186,35 @@</span><br><span>      }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* Callback function to be called when the RSIP ("Reset in Progress") mgcp</span><br><span style="color: hsl(0, 100%, 40%);">- * command is received */</span><br><span style="color: hsl(0, 100%, 40%);">-static int mgcp_rsip_cb(struct mgcp_trunk *trunk)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Set flag so that, when read_call_agent() is called next time</span><br><span style="color: hsl(0, 100%, 40%);">-  * the reset can progress */</span><br><span style="color: hsl(0, 100%, 40%);">-    reset_endpoints = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    reset_trunk = trunk;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static int read_call_agent(struct osmo_fd *fd, unsigned int what)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct osmo_sockaddr addr;</span><br><span style="color: hsl(0, 100%, 40%);">-      socklen_t slen = sizeof(addr);</span><br><span style="color: hsl(0, 100%, 40%);">-  struct msgb *msg;</span><br><span>    struct msgb *resp;</span><br><span>   int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  msg = (struct msgb *) fd->data;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct to_trunkthread_mgcp_msg w = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+       socklen_t slen = sizeof(w.x.addr);</span><br><span> </span><br><span>       /* read one less so we can use it as a \0 */</span><br><span style="color: hsl(0, 100%, 40%);">-    int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,</span><br><span style="color: hsl(0, 100%, 40%);">-          (struct sockaddr *) &addr, &slen);</span><br><span style="color: hsl(120, 100%, 40%);">+    ssize_t rc = recvfrom(cfg->gw_fd.bfd.fd, w.msg, sizeof(w.msg)-1, 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                (struct sockaddr *) &w.x.addr, &slen);</span><br><span>       if (rc < 0) {</span><br><span>             perror("Gateway failed to read");</span><br><span>          return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      } else if (slen > sizeof(addr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (slen > sizeof(w.x.addr)) {</span><br><span>             fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                        (size_t) slen, sizeof(addr));</span><br><span style="color: hsl(120, 100%, 40%);">+                 (size_t) slen, sizeof(w.x.addr));</span><br><span>            return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* handle message now */</span><br><span style="color: hsl(0, 100%, 40%);">-        msg->l2h = msgb_put(msg, rc);</span><br><span style="color: hsl(0, 100%, 40%);">-        resp = mgcp_handle_message(cfg, msg);</span><br><span style="color: hsl(0, 100%, 40%);">-   msgb_reset(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      w.x.pdata.cfg = cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+  w.x.msglen = rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    resp = mgcp_submit_message_to_trunkthread(cfg, &w);</span><br><span> </span><br><span>  if (resp) {</span><br><span style="color: hsl(0, 100%, 40%);">-             sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, &addr.u.sa, sizeof(addr));</span><br><span style="color: hsl(120, 100%, 40%);">+                sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, &w.x.addr.u.sa, sizeof(w.x.addr));</span><br><span>               msgb_free(resp);</span><br><span>     }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* reset endpoints */</span><br><span style="color: hsl(0, 100%, 40%);">-   if (reset_endpoints) {</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGP(DLMGCP, LOGL_NOTICE,</span><br><span style="color: hsl(0, 100%, 40%);">-                    "Asked to reset endpoints: %u/%d\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                  reset_trunk->trunk_nr, reset_trunk->trunk_type);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-             /* reset flag */</span><br><span style="color: hsl(0, 100%, 40%);">-                reset_endpoints = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-            /* Walk over all endpoints and trigger a release, this will release all</span><br><span style="color: hsl(0, 100%, 40%);">-          * endpoints, possible open connections are forcefully dropped */</span><br><span style="color: hsl(0, 100%, 40%);">-               for (i = 0; i < reset_trunk->number_endpoints; ++i)</span><br><span style="color: hsl(0, 100%, 40%);">-                       mgcp_endp_release(reset_trunk->endpoints[i]);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -324,6 +292,10 @@</span><br><span>    osmo_init_logging2(tall_mgw_ctx, &log_info);</span><br><span>     libosmo_abis_init(tall_mgw_ctx);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        rate_ctr_init(tall_mgw_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_stats_init(tall_mgw_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     cfg = mgcp_config_alloc();</span><br><span>   if (!cfg)</span><br><span>            return -1;</span><br><span>@@ -334,23 +306,33 @@</span><br><span>   vty_info.usr_attr_letters[MGW_CMD_ATTR_NEWCONN] = 'n';</span><br><span> </span><br><span>   vty_init(&vty_info);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_cpu_sched_vty_init(tall_mgw_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_cpu_sched_force_apply_later(true);</span><br><span>      logging_vty_add_cmds();</span><br><span>      osmo_talloc_vty_add_cmds();</span><br><span>  osmo_stats_vty_add_cmds();</span><br><span>   mgcp_vty_init();</span><br><span>     ctrl_vty_init(cfg);</span><br><span>  e1inp_vty_init();</span><br><span style="color: hsl(0, 100%, 40%);">-       osmo_cpu_sched_vty_init(tall_mgw_ctx);</span><br><span> </span><br><span>   handle_options(argc, argv);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- rate_ctr_init(tall_mgw_ctx);</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_stats_init(tall_mgw_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_disable_null_tracking();</span><br><span style="color: hsl(120, 100%, 40%);">+       log_enable_multithread();</span><br><span> </span><br><span>        rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);</span><br><span>  if (rc < 0)</span><br><span>               return rc;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        if (cfg->global_osmux_options.osmux) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct mgcp_trunk *trunk;</span><br><span style="color: hsl(120, 100%, 40%);">+             fprintf(stderr, "Osmux usage impossible with threads! Disabling threads..\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry(trunk, &cfg->trunks, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     trunk->use_threads = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                        trunk->num_threads = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* start telnet after reading config for vty_get_bind_addr() */</span><br><span>      rc = telnet_init_dynif(tall_mgw_ctx, NULL,</span><br><span>                          vty_get_bind_addr(), OSMO_VTY_PORT_MGW);</span><br><span>@@ -363,9 +345,7 @@</span><br><span>                        ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);</span><br><span>       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Set the reset callback function. This functions is called when the</span><br><span style="color: hsl(0, 100%, 40%);">-    * mgcp-command "RSIP" (Reset in Progress) is received */</span><br><span style="color: hsl(0, 100%, 40%);">-     cfg->reset_cb = mgcp_rsip_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+      split_trunks_into_threads(cfg);</span><br><span> </span><br><span>  /* we need to bind a socket */</span><br><span>       flags = OSMO_SOCK_F_BIND;</span><br><span>@@ -381,11 +361,7 @@</span><br><span>     }</span><br><span> </span><br><span>        cfg->gw_fd.bfd.cb = read_call_agent;</span><br><span style="color: hsl(0, 100%, 40%);">- cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!cfg->gw_fd.bfd.data) {</span><br><span style="color: hsl(0, 100%, 40%);">-          fprintf(stderr, "Gateway memory error.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-           return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(120, 100%, 40%);">+     cfg->gw_fd.bfd.data = 0;</span><br><span> </span><br><span>      LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",</span><br><span>             cfg->source_addr, cfg->source_port);</span><br><span>diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c</span><br><span>index a8aad14..0eccc63 100644</span><br><span>--- a/tests/mgcp/mgcp_test.c</span><br><span>+++ b/tests/mgcp/mgcp_test.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> #include <osmocom/mgcp/mgcp_sdp.h></span><br><span> #include <osmocom/mgcp/mgcp_codec.h></span><br><span> #include <osmocom/mgcp/mgcp_network.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mgcp/mgcp_threads.h></span><br><span> </span><br><span> #include <osmocom/core/application.h></span><br><span> #include <osmocom/core/talloc.h></span><br><span>@@ -761,6 +762,24 @@</span><br><span>         return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb * handle_message_threadwrapper(struct mgcp_config *cfg, struct mgcp_trunk *trunk, struct msgb *inp, const char* testname) {</span><br><span style="color: hsl(120, 100%, 40%);">+          struct msgb * msg;</span><br><span style="color: hsl(120, 100%, 40%);">+            struct to_trunkthread_mgcp_msg w = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+               struct per_thread_info inf = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           trunk->thread_info = &inf;</span><br><span style="color: hsl(120, 100%, 40%);">+             w.x.pdata.cfg = cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+          w.x.msglen = msgb_l2len(inp);</span><br><span style="color: hsl(120, 100%, 40%);">+         memcpy(w.msg, inp->data, msgb_l2len(inp) );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              msg = mgcp_handle_message(cfg, &w);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (msg || !w.x.successfully_parsed)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+           msg = thread_handle_mgcp_message(&w, &inf);</span><br><span style="color: hsl(120, 100%, 40%);">+           return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void test_messages(void)</span><br><span> {</span><br><span>     struct mgcp_config *cfg;</span><br><span>@@ -770,7 +789,7 @@</span><br><span>       struct mgcp_conn_rtp *conn = NULL;</span><br><span>   char last_conn_id[256];</span><br><span>      int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- char *last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span style="color: hsl(120, 100%, 40%);">+    char *last_endpoint;</span><br><span> </span><br><span>     cfg = mgcp_config_alloc();</span><br><span>   trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);</span><br><span>@@ -794,7 +813,8 @@</span><br><span>                                           t->extra_fmtp);</span><br><span> </span><br><span>            inp = create_msg(t->req, last_conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-              msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+          msg = handle_message_threadwrapper(cfg, trunk, inp, t->name);</span><br><span style="color: hsl(120, 100%, 40%);">+              last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>                 msgb_free(inp);</span><br><span>              if (!t->exp_resp) {</span><br><span>                       if (msg) {</span><br><span>@@ -924,7 +944,7 @@</span><br><span>             printf("Testing %s\n", t->name);</span><br><span> </span><br><span>            inp = create_msg(t->req, last_conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-              msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+          msg = handle_message_threadwrapper(cfg, trunk, inp, t->name);</span><br><span> </span><br><span>                 msgb_free(inp);</span><br><span>              if (msg && check_response(msg->data, t->exp_resp) != 0) {</span><br><span>@@ -944,7 +964,7 @@</span><br><span>                /* Retransmit... */</span><br><span>          printf("Re-transmitting %s\n", t->name);</span><br><span>                inp = create_msg(t->req, last_conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-              msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+          msg = handle_message_threadwrapper(cfg, trunk, inp, t->name);</span><br><span>             msgb_free(inp);</span><br><span>              if (check_response(msg->data, t->exp_resp) != 0) {</span><br><span>                     printf("%s failed '%s'\n", t->name, (char *)msg->data);</span><br><span>@@ -979,7 +999,7 @@</span><br><span>         mgcp_trunk_equip(trunk);</span><br><span> </span><br><span>        inp = create_msg(CRCX, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-   msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+  msg = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span>       OSMO_ASSERT(msg);</span><br><span>    OSMO_ASSERT(get_conn_id_from_response(msg->data, conn_id,</span><br><span>                                               sizeof(conn_id)) == 0);</span><br><span>@@ -988,7 +1008,7 @@</span><br><span> </span><br><span>     /* send the RQNT and check for the CB */</span><br><span>     inp = create_msg(RQNT, conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-        msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+  msg = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span>       if (strncmp((const char *)msg->l2h, "200", 3) != 0) {</span><br><span>           printf("FAILED: message is not 200. '%s'\n", msg->l2h);</span><br><span>                 abort();</span><br><span>@@ -1003,7 +1023,7 @@</span><br><span>     msgb_free(inp);</span><br><span> </span><br><span>  inp = create_msg(DLCX, conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-        msgb_free(mgcp_handle_message(cfg, inp));</span><br><span style="color: hsl(120, 100%, 40%);">+     msg = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span>       msgb_free(inp);</span><br><span>      mgcp_endpoints_release(trunk);</span><br><span>       talloc_free(cfg);</span><br><span>@@ -1386,7 +1406,7 @@</span><br><span>    struct in_addr addr;</span><br><span>         struct mgcp_conn_rtp *conn = NULL;</span><br><span>   char conn_id[256];</span><br><span style="color: hsl(0, 100%, 40%);">-      char *last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span style="color: hsl(120, 100%, 40%);">+    char* last_endpoint;</span><br><span> </span><br><span>     printf("Testing multiple payload types\n");</span><br><span> </span><br><span>@@ -1396,9 +1416,9 @@</span><br><span>         mgcp_trunk_equip(trunk);</span><br><span> </span><br><span>    /* Allocate endpoint 1@mgw with two codecs */</span><br><span style="color: hsl(0, 100%, 40%);">-   last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_1, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1412,9 +1432,9 @@</span><br><span>      OSMO_ASSERT(conn->end.codec->payload_type == 18);</span><br><span> </span><br><span>  /* Allocate 2@mgw with three codecs, last one ignored */</span><br><span style="color: hsl(0, 100%, 40%);">-        last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_2, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1433,9 +1453,9 @@</span><br><span>       * it makes and since we already decided in OS#2658 that a missing</span><br><span>    * LCO should pick a sane default codec, it makes sense to expect</span><br><span>     * the same behaviour if SDP lacks proper payload type information */</span><br><span style="color: hsl(0, 100%, 40%);">-   last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_3, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1449,9 +1469,9 @@</span><br><span>      OSMO_ASSERT(conn->end.codec->payload_type == 0);</span><br><span> </span><br><span>   /* Allocate 4@mgw with a single codec */</span><br><span style="color: hsl(0, 100%, 40%);">-        last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_4, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1465,10 +1485,10 @@</span><br><span>    OSMO_ASSERT(conn->end.codec->payload_type == 18);</span><br><span> </span><br><span>  /* Allocate 5@mgw and let osmo-mgw pick a codec from the list */</span><br><span style="color: hsl(0, 100%, 40%);">-        last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);</span><br><span>         trunk->no_audio_transcoding = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-     resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1482,8 +1502,8 @@</span><br><span>      OSMO_ASSERT(conn->end.codec->payload_type == 0);</span><br><span> </span><br><span>   inp = create_msg(MDCX_NAT_DUMMY, conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-      last_endpoint[0] = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-        resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         msgb_free(inp);</span><br><span>      msgb_free(resp);</span><br><span>     OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);</span><br><span>@@ -1509,10 +1529,10 @@</span><br><span>       conn = mgcp_conn_get_rtp(endp, conn_id);</span><br><span>     OSMO_ASSERT(!conn);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- last_endpoint[0] = '\0';</span><br><span>     inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);</span><br><span>         trunk->no_audio_transcoding = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-     resp = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+ resp = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span style="color: hsl(120, 100%, 40%);">+       last_endpoint = mgcp_debug_get_last_endpoint_name();</span><br><span>         OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,</span><br><span>                                              sizeof(conn_id)) == 0);</span><br><span>        msgb_free(inp);</span><br><span>@@ -1596,7 +1616,7 @@</span><br><span>         mgcp_trunk_equip(trunk);</span><br><span> </span><br><span>      inp = create_msg(CRCX, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-   msg = mgcp_handle_message(cfg, inp);</span><br><span style="color: hsl(120, 100%, 40%);">+  msg = handle_message_threadwrapper(cfg, trunk, inp, "__FILE__""__LINE__");</span><br><span> </span><br><span>   if (check_response(msg->data, CRCX_RET_NO_RTPMAP) != 0) {</span><br><span>                 printf("FAILED: there should not be a RTPMAP: %s\n",</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-mgw/+/25432">change 25432</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-mgw/+/25432"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-mgw </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I31be8253600c8af0a43c967d0d128f5ba7b16260 </div>
<div style="display:none"> Gerrit-Change-Number: 25432 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Hoernchen <ewild@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>