aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-12-24 21:39:55 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-12-26 09:40:03 +0100
commit17164061a631de73378b677cd4a267351af22dbe (patch)
treeaa5076cb9317998906d4fb69ca00d9b97e61538e
parent11b28f9c26cdafe448eae1dbf6b06a7d1050083a (diff)
sms: Begin sending SMS messages, handle failures
The sms queue will attempt to send one SMS per subscriber to fill all the available slots. It will handle the case where paging has not started, timed out or if there was any kind of other failure. It is also retransmitting SMS in case of failures.
-rw-r--r--openbsc/include/openbsc/sms_queue.h2
-rw-r--r--openbsc/src/sms_queue.c300
2 files changed, 300 insertions, 2 deletions
diff --git a/openbsc/include/openbsc/sms_queue.h b/openbsc/include/openbsc/sms_queue.h
index d377aca40..d04856629 100644
--- a/openbsc/include/openbsc/sms_queue.h
+++ b/openbsc/include/openbsc/sms_queue.h
@@ -2,7 +2,9 @@
#define SMS_QUEUE_H
struct gsm_network;
+struct gsm_sms_queue;
int sms_queue_start(struct gsm_network *, int in_flight);
+int sms_queue_trigger(struct gsm_sms_queue *);
#endif
diff --git a/openbsc/src/sms_queue.c b/openbsc/src/sms_queue.c
index f61cf6326..f6d09cf76 100644
--- a/openbsc/src/sms_queue.c
+++ b/openbsc/src/sms_queue.c
@@ -35,17 +35,225 @@
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_subscriber.h>
#include <openbsc/signal.h>
#include <osmocore/talloc.h>
+/*
+ * One pending SMS that we wait for.
+ */
+struct gsm_sms_pending {
+ struct llist_head entry;
+
+ struct gsm_subscriber *subscr;
+ unsigned long long sms_id;
+ int failed_attempts;
+ int resend;
+};
struct gsm_sms_queue {
+ struct timer_list resend_pending;
+ struct timer_list push_queue;
struct gsm_network *network;
int max_pending;
int pending;
+
+ struct llist_head pending_sms;
+ unsigned long long last_subscr_id;
};
+static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
+static int sms_sms_cb(unsigned int, unsigned int, void *, void *);
+
+static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq,
+ struct gsm_sms *sms)
+{
+ struct gsm_sms_pending *pending;
+
+ llist_for_each_entry(pending, &smsq->pending_sms, entry) {
+ if (pending->sms_id == sms->id)
+ return pending;
+ }
+
+ return NULL;
+}
+
+static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms)
+{
+ return sms_find_pending(smsq, sms) != NULL;
+}
+
+static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
+ struct gsm_subscriber *subscr)
+{
+ struct gsm_sms_pending *pending;
+
+ llist_for_each_entry(pending, &smsq->pending_sms, entry) {
+ if (pending->subscr == subscr)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
+ struct gsm_sms *sms)
+{
+ struct gsm_sms_pending *pending;
+
+ pending = talloc_zero(smsq, struct gsm_sms_pending);
+ if (!pending)
+ return NULL;
+
+ pending->subscr = subscr_get(sms->receiver);
+ pending->sms_id = sms->id;
+ return pending;
+}
+
+static void sms_pending_free(struct gsm_sms_pending *pending)
+{
+ subscr_put(pending->subscr);
+ llist_del(&pending->entry);
+ talloc_free(pending);
+}
+
+static void sms_pending_resend(struct gsm_sms_pending *pending)
+{
+ struct gsm_sms_queue *smsq;
+ LOGP(DSMS, LOGL_DEBUG,
+ "Scheduling resend of SMS %llu.\n", pending->sms_id);
+
+ pending->resend = 1;
+
+ smsq = pending->subscr->net->sms_queue;
+ if (bsc_timer_pending(&smsq->resend_pending))
+ return;
+
+ bsc_schedule_timer(&smsq->resend_pending, 1, 0);
+}
+
+static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
+{
+ struct gsm_sms_queue *smsq;
+
+ LOGP(DSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
+ pending->sms_id, pending->failed_attempts);
+
+ smsq = pending->subscr->net->sms_queue;
+ if (++pending->failed_attempts < 3)
+ return sms_pending_resend(pending);
+
+ if (paging_error) {
+ LOGP(DSMS, LOGL_NOTICE,
+ "Subscriber %llu is not reachable. Setting LAC=0.\n", pending->subscr->id);
+ pending->subscr->lac = GSM_LAC_RESERVED_DETACHED;
+ db_sync_subscriber(pending->subscr);
+ }
+
+ sms_pending_free(pending);
+ smsq->pending -= 1;
+ sms_queue_trigger(smsq);
+}
+
+/*
+ * Resend all SMS that are scheduled for a resend. This is done to
+ * avoid an immediate failure.
+ */
+static void sms_resend_pending(void *_data)
+{
+ struct gsm_sms_pending *pending, *tmp;
+ struct gsm_sms_queue *smsq = _data;
+
+ llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
+ struct gsm_sms *sms;
+ if (!pending->resend)
+ continue;
+
+ sms = db_sms_get(smsq->network, pending->sms_id);
+
+ /* the sms is gone? Move to the next */
+ if (!sms) {
+ sms_pending_free(pending);
+ smsq->pending -= 1;
+ sms_queue_trigger(smsq);
+ } else {
+ pending->resend = 0;
+ gsm411_send_sms_subscr(sms->receiver, sms);
+ }
+ }
+}
+
+/**
+ * I will submit up to max_pending - pending SMS to the
+ * subsystem.
+ */
+static void sms_submit_pending(void *_data)
+{
+ struct gsm_sms_queue *smsq = _data;
+ int attempts = smsq->max_pending - smsq->pending;
+ int i;
+
+ LOGP(DSMS, LOGL_NOTICE, "Attempting to send %d SMS\n", attempts);
+
+ for (i = 0; i < attempts; ++i) {
+ struct gsm_sms_pending *pending;
+ struct gsm_sms *sms;
+
+ sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10);
+
+ /* handle wrapping around */
+ if (!sms) {
+ smsq->last_subscr_id = 0;
+ sms = db_sms_get_unsent_by_subscr(smsq->network,
+ smsq->last_subscr_id, 10);
+ }
+
+ if (!sms)
+ break;
+
+ /* no need to send a pending sms */
+ if (sms_is_in_pending(smsq, sms)) {
+ LOGP(DSMS, LOGL_DEBUG,
+ "SMSqueue with pending sms: %llu\n. Skipping", sms->id);
+ sms_free(sms);
+ continue;
+ }
+
+ /* no need to send a SMS with the same receiver */
+ if (sms_subscriber_is_pending(smsq, sms->receiver)) {
+ LOGP(DSMS, LOGL_DEBUG,
+ "SMSqueue with pending sub: %llu. Skipping\n", sms->receiver->id);
+ sms_free(sms);
+ continue;
+ }
+
+ pending = sms_pending_from(smsq, sms);
+ if (!pending) {
+ LOGP(DSMS, LOGL_ERROR,
+ "Failed to create pending SMS entry.\n");
+ sms_free(sms);
+ continue;
+ }
+
+ smsq->last_subscr_id = sms->receiver->id + 1;
+ smsq->pending += 1;
+ llist_add(&pending->entry, &smsq->pending_sms);
+ gsm411_send_sms_subscr(sms->receiver, sms);
+ }
+}
+
+/*
+ * Kick off the queue again.
+ */
+int sms_queue_trigger(struct gsm_sms_queue *smsq)
+{
+ if (bsc_timer_pending(&smsq->push_queue))
+ return 0;
+
+ bsc_schedule_timer(&smsq->push_queue, 1, 0);
+ return 0;
+}
int sms_queue_start(struct gsm_network *network, int max_pending)
{
@@ -55,9 +263,19 @@ int sms_queue_start(struct gsm_network *network, int max_pending)
return -1;
}
+ register_signal_handler(SS_SUBSCR, sms_subscr_cb, network);
+ register_signal_handler(SS_SMS, sms_sms_cb, network);
+
network->sms_queue = sms;
+ INIT_LLIST_HEAD(&sms->pending_sms);
sms->network = network;
sms->max_pending = max_pending;
+ sms->push_queue.data = sms;
+ sms->push_queue.cb = sms_submit_pending;
+ sms->resend_pending.data = sms;
+ sms->resend_pending.cb = sms_resend_pending;
+
+ sms_submit_pending(sms);
return 0;
}
@@ -91,7 +309,85 @@ static int sms_subscr_cb(unsigned int subsys, unsigned int signal,
return sub_ready_for_sm(subscr);
}
-static __attribute__((constructor)) void on_dso_load_sms(void)
+static int sms_sms_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
{
- register_signal_handler(SS_SUBSCR, sms_subscr_cb, NULL);
+ struct gsm_network *network = handler_data;
+ struct sms_signal_data *sig_sms = signal_data;
+ struct gsm_sms_pending *pending;
+
+ /* We got a new SMS and maybe should launch the queue again. */
+ if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
+ sms_queue_trigger(network->sms_queue);
+ return 0;
+ }
+
+ if (!sig_sms->sms)
+ return -1;
+
+
+ /*
+ * Find the entry of our queue. The SMS subsystem will submit
+ * sms that are not in our control as we just have a channel
+ * open anyway.
+ */
+ pending = sms_find_pending(network->sms_queue, sig_sms->sms);
+ if (!pending)
+ return 0;
+
+ switch (signal) {
+ case S_SMS_DELIVERED:
+ /*
+ * Create place for a new SMS but keep the pending data
+ * so we will not attempt to send the SMS for this subscriber
+ * as we still have an open channel and will attempt to submit
+ * SMS to it anyway.
+ */
+ network->sms_queue->pending -= 1;
+ sms_submit_pending(network->sms_queue);
+ sms_pending_free(pending);
+ break;
+ case S_SMS_MEM_EXCEEDED:
+ network->sms_queue->pending -= 1;
+ sms_pending_free(pending);
+ sms_queue_trigger(network->sms_queue);
+ break;
+ case S_SMS_UNKNOWN_ERROR:
+ /*
+ * There can be many reasons for this failure. E.g. the paging
+ * timed out, the subscriber was not paged at all, or there was
+ * a protocol error. The current strategy is to try sending the
+ * next SMS for busy/oom and to retransmit when we have paged.
+ *
+ * When the paging expires three times we will disable the
+ * subscriber. If we have some kind of other transmit error we
+ * should flag the SMS as bad.
+ */
+ switch (sig_sms->paging_result) {
+ case 0:
+ /* BAD SMS? */
+ db_sms_inc_deliver_attempts(sig_sms->sms);
+ sms_pending_failed(pending, 0);
+ break;
+ case GSM_PAGING_EXPIRED:
+ sms_pending_failed(pending, 1);
+ break;
+
+ case GSM_PAGING_OOM:
+ case GSM_PAGING_BUSY:
+ network->sms_queue->pending -= 1;
+ sms_pending_free(pending);
+ sms_queue_trigger(network->sms_queue);
+ break;
+ default:
+ LOGP(DSMS, LOGL_ERROR, "Unhandled result: %d\n",
+ sig_sms->paging_result);
+ }
+ break;
+ default:
+ LOGP(DSMS, LOGL_ERROR, "Unhandled result: %d\n",
+ sig_sms->paging_result);
+ }
+
+ return 0;
}