From 69a4cf27318f471a70759a80923a0dfbbe0a8e0a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 3 May 2010 20:55:10 +0200 Subject: [gprs] NS: improved timer handling for RESET --- openbsc/include/openbsc/gprs_ns.h | 4 +- openbsc/src/gprs_ns.c | 93 ++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 17 deletions(-) (limited to 'openbsc') diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h index 40784049..27c54cb1 100644 --- a/openbsc/include/openbsc/gprs_ns.h +++ b/openbsc/include/openbsc/gprs_ns.h @@ -122,8 +122,8 @@ enum nsvc_timer_mode { /* standard timers */ NSVC_TIMER_TNS_TEST, NSVC_TIMER_TNS_ALIVE, - /* custom timer */ - NSVC_TIMER_RESET, + NSVC_TIMER_TNS_RESET, + _NSVC_TIMER_NR, }; struct gprs_nsvc { diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c index 9c198ad9..39ca8101 100644 --- a/openbsc/src/gprs_ns.c +++ b/openbsc/src/gprs_ns.c @@ -109,6 +109,8 @@ static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, return NULL; } +static void gprs_ns_timer_cb(void *data); + static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) { struct gprs_nsvc *nsvc; @@ -118,6 +120,9 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) /* before RESET procedure: BLOCKED and DEAD */ nsvc->state = NSE_S_BLOCKED; nsvc->nsi = nsi; + nsvc->timer.cb = gprs_ns_timer_cb; + nsvc->timer.data = nsvc; + llist_add(&nsvc->list, &nsi->gprs_nsvcs); return nsvc; @@ -133,7 +138,7 @@ static const struct value_string ns_cause_str[] = { { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" }, { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" }, - { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error }, unspecified" }, + { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" }, { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" }, { 0, NULL } @@ -178,10 +183,47 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) return gprs_ns_tx(nsvc, msg); } -#define NS_TIMER_ALIVE 3, 0 /* after 3 seconds without response, we retry */ -#define NS_TIMER_TEST 30, 0 /* every 10 seconds we check if the BTS is still alive */ +static int gprs_ns_tx_reset(struct gprs_nsvc *nsvc) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint8_t cause = NS_CAUSE_OM_INTERVENTION; + uint16_t nsvci = htons(nsvc->nsvci); + uint16_t nsei = htons(nsvc->nsei); + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + nsh->pdu_type = NS_PDUT_RESET; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); + + return gprs_ns_tx(nsvc, msg); + +} + #define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ +static const uint8_t timer_mode_tout[_NSVC_TIMER_NR] = { + [NSVC_TIMER_TNS_RESET] = 60, + [NSVC_TIMER_TNS_ALIVE] = 3, + [NSVC_TIMER_TNS_TEST] = 30, +}; + +static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) +{ + nsvc->alive_retries = 0; + + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + + nsvc->timer_mode = mode; + bsc_schedule_timer(&nsvc->timer, timer_mode_tout[mode], 0); +} + static void gprs_ns_timer_cb(void *data) { struct gprs_nsvc *nsvc = data; @@ -193,20 +235,26 @@ static void gprs_ns_timer_cb(void *data) if (nsvc->alive_retries > NS_ALIVE_RETRIES) { /* mark as dead and blocked */ nsvc->state = NSE_S_BLOCKED; - DEBUGP(DGPRS, "Tns-alive more then %u retries, " - " blocking NS-VC\n", NS_ALIVE_RETRIES); + DEBUGP(DGPRS, "NSEI=%u Tns-alive expired more then " + "%u times, blocking NS-VC\n", nsvc->nsei, + NS_ALIVE_RETRIES); /* FIXME: inform higher layers */ return; } + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_TEST: /* Tns-test case: send NS-ALIVE PDU */ gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); /* start Tns-alive timer */ - nsvc->timer_mode = NSVC_TIMER_TNS_ALIVE; + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_RESET: + /* Chapter 7.3: Re-send the RESET */ + gprs_ns_tx_reset(nsvc); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); break; } - bsc_schedule_timer(&nsvc->timer, NS_TIMER_ALIVE); } /* Section 9.2.6 */ @@ -249,6 +297,17 @@ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) return -EINVAL; } + if (!(nsvc->state & NSE_S_ALIVE)) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + if (nsvc->state & NSE_S_BLOCKED) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); if (!nsh) { LOGP(DGPRS, LOGL_ERROR, "Not enough headroom for NS header\n"); @@ -333,9 +392,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) /* mark the NS-VC as blocked and alive */ /* start the test procedure */ - nsvc->timer.cb = gprs_ns_timer_cb; - nsvc->timer.data = nsvc; - bsc_schedule_timer(&nsvc->timer, NS_TIMER_ALIVE); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); return gprs_ns_tx_reset_ack(nsvc); } @@ -375,8 +432,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, /* stop Tns-alive */ bsc_del_timer(&nsvc->timer); /* start Tns-test */ - nsvc->timer_mode = NSVC_TIMER_TNS_TEST; - bsc_schedule_timer(&nsvc->timer, NS_TIMER_TEST); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); break; case NS_PDUT_UNITDATA: /* actual user data */ @@ -392,6 +448,10 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, DEBUGP(DGPRS, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); /* mark remote NS-VC as blocked + active */ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + if (nsvc->remote_end_is_sgsn) { + /* stop RESET timer */ + bsc_del_timer(&nsvc->timer); + } break; case NS_PDUT_UNBLOCK: /* Section 7.2: unblocking procedure */ @@ -561,9 +621,12 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, nsvc->remote_end_is_sgsn = 1; /* Initiate a RESET procedure */ - if (gprs_ns_tx_simple(nsvc, NS_PDUT_RESET) < 0) - return NULL; - /* FIXME: should we run a timer and re-transmit the reset request? */ + if (gprs_ns_tx_reset(nsvc) < 0) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", + nsei); + } + /* run a timer and re-transmit the reset request? */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); return nsvc; } -- cgit v1.2.3