[gprs] NS: improved timer handling for RESET

This commit is contained in:
Harald Welte 2010-05-03 20:55:10 +02:00
parent 80405458a0
commit 69a4cf2731
2 changed files with 80 additions and 17 deletions

View File

@ -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 {

View File

@ -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;
}