Module: sip-router Branch: master Commit: 6fb686de57f783b3c29e5b4e11ad1509882616bb URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=6fb686de...
Author: Daniel-Constantin Mierla miconda@gmail.com Committer: Daniel-Constantin Mierla miconda@gmail.com Date: Thu Aug 14 17:49:18 2014 +0200
dialog: when adding a new dialog, lock the slot until the structure is linked
- search for dialog based on sip attributes and if no result found, then lock the hash table slot until the new structure is built and linked in the table - should avoid simulataneous creation for same dialog if there is a not-handled retransmission or parallel forking upstream, resulting in many processing managing duplicated requests
---
modules/dialog/dlg_db_handler.c | 2 +- modules/dialog/dlg_handlers.c | 45 +++++++++++++++----------- modules/dialog/dlg_hash.c | 65 +++++++++++++++++++++++++++++++++++--- modules/dialog/dlg_hash.h | 31 ++++++++++++++++++- 4 files changed, 116 insertions(+), 27 deletions(-)
diff --git a/modules/dialog/dlg_db_handler.c b/modules/dialog/dlg_db_handler.c index f61aae5..7e83729 100644 --- a/modules/dialog/dlg_db_handler.c +++ b/modules/dialog/dlg_db_handler.c @@ -360,7 +360,7 @@ static int load_dialog_info_from_db(int dlg_hash_size, int fetch_num_rows) }
/*link the dialog*/ - link_dlg(dlg, 0); + link_dlg(dlg, 0, 0);
dlg->h_id = VAL_INT(values+1); next_id = d_table->entries[dlg->h_entry].next_id; diff --git a/modules/dialog/dlg_handlers.c b/modules/dialog/dlg_handlers.c index a0daa39..093b774 100644 --- a/modules/dialog/dlg_handlers.c +++ b/modules/dialog/dlg_handlers.c @@ -776,6 +776,7 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) str ttag; str req_uri; unsigned int dir; + int mlock;
dlg = dlg_get_ctx_dialog(); if(dlg != NULL) { @@ -800,18 +801,20 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) } trim(&req_uri);
- if (detect_spirals) - { - if (spiral_detected == 1) - return 0; - - dir = DLG_DIR_NONE; + dir = DLG_DIR_NONE; + mlock = 1; + /* search dialog by SIP attributes + * - if not found, hash table slot is left locked, to avoid races + * to add 'same' dialog on parallel forking or not-handled-yet + * retransmissions. Release slot after linking new dialog */ + dlg = search_dlg(&callid, &ftag, &ttag, &dir); + if(dlg) { + mlock = 0; + if (detect_spirals) { + if (spiral_detected == 1) + return 0;
- dlg = get_dlg(&callid, &ftag, &ttag, &dir); - if (dlg) - { - if ( dlg->state != DLG_STATE_DELETED ) - { + if ( dlg->state != DLG_STATE_DELETED ) { LM_DBG("Callid '%.*s' found, must be a spiraled request\n", callid.len, callid.s); spiral_detected = 1; @@ -819,9 +822,12 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) if (run_initial_cbs) run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0); - /* get_dlg() has incremented the ref count by 1 */ + /* set ctx dlg id shortcuts */ + _dlg_ctx.iuid.h_entry = dlg->h_entry; + _dlg_ctx.iuid.h_id = dlg->h_id; + /* search_dlg() has incremented the ref count by 1 */ dlg_release(dlg); - goto finish; + return 0; } dlg_release(dlg); } @@ -834,16 +840,16 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) &ftag/*from_tag*/, &req_uri /*r-uri*/ );
- if (dlg==0) - { + if (dlg==0) { + if(likely(mlock==1)) dlg_hash_release(&callid); LM_ERR("failed to create new dialog\n"); return -1; }
/* save caller's tag, cseq, contact and record route*/ if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG, - &(get_from(req)->tag_value)) !=0) - { + &(get_from(req)->tag_value)) !=0) { + if(likely(mlock==1)) dlg_hash_release(&callid); LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); return -1; @@ -852,7 +858,9 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) /* Populate initial varlist: */ dlg->vars = get_local_varlist_pointer(req, 1);
- link_dlg(dlg, 0); + /* if search_dlg() returned NULL, slot was kept locked */ + link_dlg(dlg, 0, mlock); + if(likely(mlock==1)) dlg_hash_release(&callid);
dlg->lifetime = get_dlg_timeout(req); s.s = _dlg_ctx.to_route_name; @@ -876,7 +884,6 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) if_update_stat( dlg_enable_stats, processed_dlgs, 1);
_dlg_ctx.cpid = my_pid(); -finish: _dlg_ctx.iuid.h_entry = dlg->h_entry; _dlg_ctx.iuid.h_id = dlg->h_id; set_current_dialog(req, dlg); diff --git a/modules/dialog/dlg_hash.c b/modules/dialog/dlg_hash.c index 8220104..1e27b3b 100644 --- a/modules/dialog/dlg_hash.c +++ b/modules/dialog/dlg_hash.c @@ -691,10 +691,12 @@ dlg_cell_t* dlg_get_by_iuid(dlg_iuid_t *diuid) * \param ftag from tag * \param ttag to tag * \param dir direction + * \param mode let hash table slot locked if dialog is not found * \return dialog structure on success, NULL on failure */ static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry, - str *callid, str *ftag, str *ttag, unsigned int *dir) + str *callid, str *ftag, str *ttag, + unsigned int *dir, int mode) { struct dlg_cell *dlg; struct dlg_entry *d_entry; @@ -714,7 +716,7 @@ static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry, } }
- dlg_unlock( d_table, d_entry); + if(likely(mode==0)) dlg_unlock( d_table, d_entry); LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); return 0; } @@ -743,7 +745,7 @@ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir) unsigned int he;
he = core_hash(callid, 0, d_table->size); - dlg = internal_get_dlg(he, callid, ftag, ttag, dir); + dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 0);
if (dlg == 0) { LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); @@ -754,17 +756,68 @@ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
/*! + * \brief Search dialog that corresponds to CallId, From Tag and To Tag + * + * Get dialog that correspond to CallId, From Tag and To Tag. + * See RFC 3261, paragraph 4. Overview of Operation: + * "The combination of the To tag, From tag, and Call-ID completely + * defines a peer-to-peer SIP relationship between [two UAs] and is + * referred to as a dialog." + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * If the dialog is not found, the hash slot is left locked, to allow + * linking the structure of a new dialog. + * \param callid callid + * \param ftag from tag + * \param ttag to tag + * \param dir direction + * \return dialog structure on success, NULL on failure (and slot locked) + */ +dlg_cell_t* search_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir) +{ + struct dlg_cell *dlg; + unsigned int he; + + he = core_hash(callid, 0, d_table->size); + dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 1); + + if (dlg == 0) { + LM_DBG("dialog with callid='%.*s' not found\n", callid->len, callid->s); + return 0; + } + return dlg; +} + + +/*! + * \brief Release hash table slot by call-id + * \param callid call-id value + */ +void dlg_hash_release(str *callid) +{ + unsigned int he; + struct dlg_entry *d_entry; + + he = core_hash(callid, 0, d_table->size); + d_entry = &(d_table->entries[he]); + dlg_unlock(d_table, d_entry); +} + + + +/*! * \brief Link a dialog structure * \param dlg dialog * \param n extra increments for the reference counter + * \param mode link in safe mode (0 - lock slot; 1 - don't) */ -void link_dlg(struct dlg_cell *dlg, int n) +void link_dlg(struct dlg_cell *dlg, int n, int mode) { struct dlg_entry *d_entry;
d_entry = &(d_table->entries[dlg->h_entry]);
- dlg_lock( d_table, d_entry); + if(unlikely(mode==0)) dlg_lock( d_table, d_entry);
/* keep id 0 for special cases */ dlg->h_id = 1 + d_entry->next_id++; @@ -780,7 +833,7 @@ void link_dlg(struct dlg_cell *dlg, int n)
ref_dlg_unsafe(dlg, 1+n);
- dlg_unlock( d_table, d_entry); + if(unlikely(mode==0)) dlg_unlock( d_table, d_entry); return; }
diff --git a/modules/dialog/dlg_hash.h b/modules/dialog/dlg_hash.h index ec3ffbb..8798ccc 100644 --- a/modules/dialog/dlg_hash.h +++ b/modules/dialog/dlg_hash.h @@ -318,11 +318,40 @@ dlg_cell_t* get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir);
/*! + * \brief Search dialog that corresponds to CallId, From Tag and To Tag + * + * Get dialog that correspond to CallId, From Tag and To Tag. + * See RFC 3261, paragraph 4. Overview of Operation: + * "The combination of the To tag, From tag, and Call-ID completely + * defines a peer-to-peer SIP relationship between [two UAs] and is + * referred to as a dialog." + * Note that the caller is responsible for decrementing (or reusing) + * the reference counter by one again if a dialog has been found. + * If the dialog is not found, the hash slot is left locked, to allow + * linking the structure of a new dialog. + * \param callid callid + * \param ftag from tag + * \param ttag to tag + * \param dir direction + * \return dialog structure on success, NULL on failure (and slot locked) + */ +dlg_cell_t* search_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir); + + +/*! + * \brief Release hash table slot by call-id + * \param callid call-id value + */ +void dlg_hash_release(str *callid); + + +/*! * \brief Link a dialog structure * \param dlg dialog * \param n extra increments for the reference counter + * \param mode link in safe mode (0 - lock slot; 1 - don't) */ -void link_dlg(dlg_cell_t *dlg, int n); +void link_dlg(struct dlg_cell *dlg, int n, int mode);
/*!