mirror of https://gerrit.osmocom.org/libosmocore
[gprs] NS: improved timer handling for RESET
This commit is contained in:
parent
80405458a0
commit
69a4cf2731
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue