summaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-05-03 20:55:10 +0200
committerHarald Welte <laforge@gnumonks.org>2010-05-04 07:20:45 +0200
commit69a4cf27318f471a70759a80923a0dfbbe0a8e0a (patch)
treebbb1c18018f8e6d4fa5d9b3ea925df971b2f8177 /openbsc
parent80405458a0c325e11e55133065df9b767bb0edc4 (diff)
[gprs] NS: improved timer handling for RESET
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/gprs_ns.h4
-rw-r--r--openbsc/src/gprs_ns.c93
2 files changed, 80 insertions, 17 deletions
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;
}