From fe5a1f1068323669c4bc112dad4cc08150d8a3bb Mon Sep 17 00:00:00 2001 From: adamdunkels Date: Sun, 10 May 2009 21:09:05 +0000 Subject: [PATCH] Added an optimization option that avoids multiple simultaneous broadcasts from neighbors: when a broadcast is to be sent, a sender does not send until it knows that no other nodes are broadcasting. --- core/net/mac/lpp.c | 175 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 158 insertions(+), 17 deletions(-) diff --git a/core/net/mac/lpp.c b/core/net/mac/lpp.c index 9640d4ec6..8e42fdd04 100644 --- a/core/net/mac/lpp.c +++ b/core/net/mac/lpp.c @@ -28,7 +28,7 @@ * * This file is part of the Contiki operating system. * - * $Id: lpp.c,v 1.21 2009/05/06 15:06:38 adamdunkels Exp $ + * $Id: lpp.c,v 1.22 2009/05/10 21:09:05 adamdunkels Exp $ */ /** @@ -78,6 +78,7 @@ #define WITH_PROBE_AFTER_TRANSMISSION 0 #define WITH_ENCOUNTER_OPTIMIZATION 1 #define WITH_ADAPTIVE_OFF_TIME 0 +#define WITH_PENDING_BROADCAST 1 #ifdef LPP_CONF_LISTEN_TIME #define LISTEN_TIME LPP_CONF_LISTEN_TIME @@ -150,10 +151,17 @@ static clock_time_t off_time = OFF_TIME; struct queue_list_item { struct queue_list_item *next; struct queuebuf *packet; - struct ctimer timer; + struct ctimer removal_timer; struct compower_activity compower; +#if WITH_PENDING_BROADCAST + uint8_t broadcast_flag; +#endif /* WITH_PENDING_BROADCAST */ }; +#define BROADCAST_FLAG_NONE 0 +#define BROADCAST_FLAG_WAITING 1 +#define BROADCAST_FLAG_PENDING 2 +#define BROADCAST_FLAG_SEND 3 LIST(pending_packets_list); LIST(queued_packets_list); @@ -250,9 +258,11 @@ turn_radio_on_for_neighbor(rimeaddr_t *neighbor, struct queue_list_item *i) struct encounter *e; if(rimeaddr_cmp(neighbor, &rimeaddr_null)) { +#if ! WITH_PENDING_BROADCAST /* We have been asked to turn on the radio for a broadcast, so we just turn on the radio. */ turn_radio_on(); +#endif /* ! WITH_PENDING_BROADCAST */ list_add(queued_packets_list, i); return; } @@ -313,7 +323,7 @@ remove_queued_packet(void *item) rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); - ctimer_stop(&i->timer); + ctimer_stop(&i->removal_timer); queuebuf_free(i->packet); list_remove(pending_packets_list, i); list_remove(queued_packets_list, i); @@ -327,6 +337,15 @@ remove_queued_packet(void *item) memb_free(&queued_packets_memb, i); } /*---------------------------------------------------------------------------*/ +#if WITH_PENDING_BROADCAST +static void +set_broadcast_flag(struct queue_list_item *i, uint8_t flag) +{ + i->broadcast_flag = flag; + ctimer_set(&i->removal_timer, PACKET_LIFETIME, remove_queued_packet, i); +} +#endif /* WITH_PENDING_BROADCAST */ +/*---------------------------------------------------------------------------*/ static void listen_callback(int periods) { @@ -379,6 +398,25 @@ send_probe(void) compower_accumulate(&compower_idle_activity); } /*---------------------------------------------------------------------------*/ +static int +num_packets_to_send(void) +{ +#if WITH_PENDING_BROADCAST + struct queue_list_item *i; + int num = 0; + + for(i = list_head(queued_packets_list); i != NULL; i = i->next) { + if(i->broadcast_flag == BROADCAST_FLAG_SEND || + i->broadcast_flag == BROADCAST_FLAG_NONE) { + ++num; + } + } + return num; +#else /* WITH_PENDING_BROADCAST */ + return list_length(queued_packets_list); +#endif /* WITH_PENDING_BROADCAST */ +} +/*---------------------------------------------------------------------------*/ /** * Duty cycle the radio and send probes. This function is called * repeatedly by a ctimer. The function restart_dutycycle() is used to @@ -393,6 +431,22 @@ dutycycle(void *ptr) while(1) { +#if WITH_PENDING_BROADCAST + { + struct queue_list_item *p; + /* Before sending the probe, we mark all broadcast packets in + our output queue to be pending. This means that they are + ready to be sent, once we know that no neighbor is + currently broadcasting. */ + for(p = list_head(queued_packets_list); p != NULL; p = p->next) { + if(p->broadcast_flag == BROADCAST_FLAG_WAITING) { + PRINTF("wait -> pending\n"); + set_broadcast_flag(p, BROADCAST_FLAG_PENDING); + } + } + } +#endif /* WITH_PENDING_BROADCAST */ + /* Send a probe packet. */ send_probe(); @@ -404,11 +458,28 @@ dutycycle(void *ptr) ctimer_set(t, LISTEN_TIME, (void (*)(void *))dutycycle, t); PT_YIELD(&dutycycle_pt); +#if WITH_PENDING_BROADCAST + { + struct queue_list_item *p; + /* Go through the list of packets we are waiting to send, and + check if there are any pending broadcasts in the list. If + there are pending broadcasts, and we did not receive any + broadcast packets from a neighbor in response to our probe, + we mark the broadcasts as being ready to send. */ + for(p = list_head(queued_packets_list); p != NULL; p = p->next) { + if(p->broadcast_flag == BROADCAST_FLAG_PENDING) { + PRINTF("pending -> send\n"); + set_broadcast_flag(p, BROADCAST_FLAG_SEND); + turn_radio_on(); + } + } + } +#endif /* WITH_PENDING_BROADCAST */ + /* If we have no packets to send (indicated by the list length of queued_packets_list being zero), we should turn the radio off. Othersize, we keep the radio on. */ - - if(list_length(queued_packets_list) == 0) { + if(num_packets_to_send() == 0) { /* If we are not listening for announcements, we turn the radio off and wait until we send the next probe. */ @@ -427,11 +498,15 @@ dutycycle(void *ptr) #endif /* WITH_ADAPTIVE_OFF_TIME */ } else { + /* We are listening for annonucements, so we count down the + listen time, and keep the radio on. */ is_listening--; ctimer_set(t, OFF_TIME, (void (*)(void *))dutycycle, t); PT_YIELD(&dutycycle_pt); } } else { + /* We had pending packets to send, so we do not turn the radio off. */ + ctimer_set(t, off_time, (void (*)(void *))dutycycle, t); PT_YIELD(&dutycycle_pt); } @@ -466,9 +541,13 @@ send_packet(void) { struct lpp_hdr hdr; clock_time_t timeout; + uint8_t is_broadcast = 0; rimeaddr_copy(&hdr.sender, &rimeaddr_node_addr); rimeaddr_copy(&hdr.receiver, packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); + if(rimeaddr_cmp(&hdr.receiver, &rimeaddr_null)) { + is_broadcast = 1; + } hdr.type = TYPE_DATA; packetbuf_hdralloc(sizeof(struct lpp_hdr)); @@ -491,7 +570,7 @@ send_packet(void) off_time = LOWEST_OFF_TIME; restart_dutycycle(off_time); #endif /* WITH_ADAPTIVE_OFF_TIME */ - + { struct queue_list_item *i; i = memb_alloc(&queued_packets_memb); @@ -501,17 +580,33 @@ send_packet(void) memb_free(&queued_packets_memb, i); return 0; } else { - - timeout = UNICAST_TIMEOUT; - if(rimeaddr_cmp(&hdr.receiver, &rimeaddr_null)) { + if(is_broadcast) { timeout = PACKET_LIFETIME; - } - ctimer_set(&i->timer, timeout, remove_queued_packet, i); +#if WITH_PENDING_BROADCAST + /* We set the broadcast state of the packet to be + waiting. This means that the packet is waiting for our + next probe to be sent. Our next probe is used to check if + there are any neighbors currently broadcasting a + packet. If so, we will get a broadcast packet in response + to our probe. If no broadcast packet is received in + response to our probe, we mark the packet as ready to be + sent. */ + set_broadcast_flag(i, BROADCAST_FLAG_WAITING); + PRINTF("-> waiting\n"); +#endif /* WITH_PENDING_BROADCAST */ + } else { + timeout = UNICAST_TIMEOUT; +#if WITH_PENDING_BROADCAST + i->broadcast_flag = BROADCAST_FLAG_NONE; +#endif /* WITH_PENDING_BROADCAST */ + } + ctimer_set(&i->removal_timer, timeout, remove_queued_packet, i); /* Wait for a probe packet from a neighbor. The actual packet transmission is handled by the read_packet() function, which receives the probe from the neighbor. */ turn_radio_on_for_neighbor(&hdr.receiver, i); + } } } @@ -540,6 +635,7 @@ read_packet(void) hdr = packetbuf_dataptr(); packetbuf_hdrreduce(sizeof(struct lpp_hdr)); /* PRINTF("got packet type %d\n", hdr->type);*/ + if(hdr->type == TYPE_PROBE) { /* Parse incoming announcements */ struct announcement_msg *adata = packetbuf_dataptr(); @@ -560,8 +656,13 @@ read_packet(void) adata->data[i].value); } + /* Register the encounter with the sending node. We now know the + neighbor's phase. */ register_encounter(&hdr->sender, reception_time); - + + /* Go through the list of packets to be sent to see if any of + them match the sender of the probe, or if they are a + broadcast packet that should be sent. */ if(list_length(queued_packets_list) > 0) { struct queue_list_item *i; for(i = list_head(queued_packets_list); i != NULL; i = i->next) { @@ -569,15 +670,34 @@ read_packet(void) qhdr = queuebuf_dataptr(i->packet); if(rimeaddr_cmp(&qhdr->receiver, &hdr->sender) || - rimeaddr_cmp(&qhdr->receiver, &rimeaddr_null)) { - PRINTF("%d.%d: got a probe from %d.%d, sending packet to %d.%d\n", + rimeaddr_cmp(&qhdr->receiver, &rimeaddr_null)) { + queuebuf_to_packetbuf(i->packet); + +#if WITH_PENDING_BROADCAST + if(i->broadcast_flag == BROADCAST_FLAG_NONE || + i->broadcast_flag == BROADCAST_FLAG_SEND) { + radio->send(queuebuf_dataptr(i->packet), + queuebuf_datalen(i->packet)); + PRINTF("%d.%d: got a probe from %d.%d, sent packet to %d.%d\n", + rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], + hdr->sender.u8[0], hdr->sender.u8[1], + qhdr->receiver.u8[0], qhdr->receiver.u8[1]); + + } else { + PRINTF("%d.%d: got a probe from %d.%d, did not send packet\n", + rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], + hdr->sender.u8[0], hdr->sender.u8[1]); + } +#else /* WITH_PENDING_BROADCAST */ + radio->send(queuebuf_dataptr(i->packet), + queuebuf_datalen(i->packet)); + PRINTF("%d.%d: got a probe from %d.%d, sent packet to %d.%d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr->sender.u8[0], hdr->sender.u8[1], qhdr->receiver.u8[0], qhdr->receiver.u8[1]); - queuebuf_to_packetbuf(i->packet); +#endif /* WITH_PENDING_BROADCAST */ + - radio->send(queuebuf_dataptr(i->packet), - queuebuf_datalen(i->packet)); /* Attribute the energy spent on listening for the probe to this packet transmission. */ @@ -626,6 +746,27 @@ read_packet(void) for the next packet. */ compower_clear(¤t_packet); +#if WITH_PENDING_BROADCAST + if(rimeaddr_cmp(&hdr->receiver, &rimeaddr_null)) { + /* This is a broadcast packet. Check the list of pending + packets to see if we are currently sending a broadcast. If + so, we refrain from sending our broadcast until one sleep + cycle period, so that the other broadcaster will have + finished sending. */ + + struct queue_list_item *i; + for(i = list_head(queued_packets_list); i != NULL; i = i->next) { + /* If the packet is a broadcast packet that is not yet + ready to be sent, we do not send it. */ + if(i->broadcast_flag == BROADCAST_FLAG_PENDING) { + PRINTF("Someone else is sending, pending -> waiting\n"); + set_broadcast_flag(i, BROADCAST_FLAG_WAITING); + } + } + } +#endif /* WITH_PENDING_BROADCAST */ + + #if WITH_PROBE_AFTER_RECEPTION /* XXX send probe after receiving a packet to facilitate data streaming. We must first copy the contents of the packetbuf into