Re: shmc/l4shmnet improvements

Stefan Fritsch <[email protected]>
Newsgroups gmane.comp.micro-kernel.l4.devel
Message-ID <[email protected]>
Here are updated versions of my patches that incorporate your suggestions.

One additional issue: I have noticed that gcc does not provide 
__sync_synchronize() on all ARM variants. I have added a workaround but it 
would be nice if L4 provided a function that causes a hardware memory barrier 
on platforms that have it, and does nothing on platforms that don't. 
l4_barrier(), l4_mb(), l4_wmb() only cause a compiler memory barrier.

On Tuesday 27 March 2012 00:45:25 Adam Lackorzynski wrote:
> > allows to set the link state up and down. The old protocol is still
> > supported.
>
> Great. I wonder if we still need the old one?

Removed.

> > +#if 0
> > +#define debug_printf(...) printf(__VA_ARGS__)
> > +#else
> > +#define debug_printf(...)
> > +#endif
>
> Please no such macros. First I'd like those always parsed so that
> they do not break: if (0) ... (or if (compile-time-fix-expression) ...).
> Then, debug_printf is just too generic.

This is not that easy because users of the header may not have printf(). 
Therefore I have just removed the debug output.

_______________________________________________
l4-hackers mailing list
[email protected]
http://os.inf.tu-dresden.de/mailman/listinfo/l4-hackers
l4shmnet.diff (text/x-diff, 17 KB)
diff --git a/src/l4linux/arch/l4/kernel/main.c b/src/l4linux/arch/l4/kernel/main.c
index d71a2b9..6064e4e 100644
--- a/src/l4linux/arch/l4/kernel/main.c
+++ b/src/l4linux/arch/l4/kernel/main.c
@@ -170,6 +170,9 @@ L4_EXTERNAL_FUNC(l4shmc_wait_chunk_to);
 L4_EXTERNAL_FUNC(l4shmc_add_chunk);
 L4_EXTERNAL_FUNC(l4shmc_add_signal);
 L4_EXTERNAL_FUNC(l4shmc_get_signal_to);
+L4_EXTERNAL_FUNC(l4shmc_iterate_chunk);
+L4_EXTERNAL_FUNC(l4shmc_area_overhead);
+L4_EXTERNAL_FUNC(l4shmc_chunk_overhead);
 #endif
 
 #ifdef CONFIG_L4_SERVER
diff --git a/src/l4linux/drivers/net/l4shmnet.c b/src/l4linux/drivers/net/l4shmnet.c
index 140bb56..e590a62 100644
--- a/src/l4linux/drivers/net/l4shmnet.c
+++ b/src/l4linux/drivers/net/l4shmnet.c
@@ -13,6 +13,7 @@
 #include <asm/generic/do_irq.h>
 
 #include <l4/shmc/shmc.h>
+#include <l4/shmc/shmbuf.h>
 
 MODULE_AUTHOR("Adam Lackorzynski <[email protected]>");
 MODULE_DESCRIPTION("L4shmnet driver");
@@ -25,7 +26,6 @@ enum {
 
 static int shmsize = 1 << 20;
 
-static char devs_create[NR_OF_DEVS];
 static char devs_to_add_name[NR_OF_DEVS][20];
 static char devs_to_add_macpart[NR_OF_DEVS];
 static int  devs_to_add_pos;
@@ -41,10 +41,10 @@ struct l4x_l4shmc_priv {
 	l4shmc_chunk_t             tx_chunk;
 	l4shmc_chunk_t             rx_chunk;
 
-	char                      *tx_ring_start;
-	char                      *rx_ring_start;
-	unsigned long              tx_ring_size;
-	unsigned long              rx_ring_size;
+	struct l4shm_buf           sb;
+	unsigned int               num:8;
+	unsigned int               remote_attached:1;
+	unsigned int               link_up:1;
 };
 
 struct l4x_l4shmc_netdev {
@@ -62,69 +62,172 @@ struct ring_chunk_head {
 	unsigned long size; // 0 == not used,  >= 0 valid chunk
 };
 
+/****************************************************************************
+ * Protocol for attaching:
+ *
+ *   1. Both parties will try to create the shm segment. The first one wins.
+ *   2. Both parties create a chunk and sig named after their MAC address
+ *      and use it as their rx side. They will set the chunk state to "clear"
+ *      which means "link down".
+ *   3. Both parties will look for another chunk. If there is one, they will
+ *      connect it as their tx side and trigger that signal.
+ *   4. The first one won't yet find the other's chunk but he will be alerted
+ *      by the second's signal and then try again. He will then connect the
+ *      chunk and sig as his tx side, and trigger the signal.
+ *   Due to the signal, there is no need to actively poll for the arrival of
+ *   the second partner.
+ *   If one partner wants to set the interface up, he sets the state of his
+ *   rx chunk to "ready" and triggers the tx signal. If both chunks are
+ *   "ready" the link state is defined to be "up".
+ *   The interface state can be set down again in the same way.
+ *   The MAC addresses of both parties must be different.
+ ****************************************************************************/
+
 static inline int chunk_size(l4shmc_area_t *s)
 {
-	return (l4shmc_area_size(s) / 2) - 52;
+	return (l4shmc_area_size(s) - l4shmc_area_overhead()) / 2 -
+	       l4shmc_chunk_overhead();
 }
 
-static int l4x_l4shmc_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+static int init_dev_self(struct net_device *dev)
 {
-	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
-	short length = skb->len;
-	struct chunk_head *chhead;
-	struct ring_chunk_head *rph;
-	unsigned long l, offs, nextoffs, r;
-	L4XV_V(f);
+	struct l4x_l4shmc_priv *priv = netdev_priv(dev);
+	int ret;
+	char myname[15];
+
+	snprintf(myname, sizeof(myname), "%pm", dev->dev_addr);
+
+	if ((ret = l4shmc_add_chunk(&priv->shmcarea, myname,
+	                            chunk_size(&priv->shmcarea),
+	                            &priv->rx_chunk))) {
+		if (ret != -L4_EEXIST) {
+			printk(KERN_WARNING "%s: Can't create chunk: %d",
+			       dev->name, ret);
+			return ret;
+		}
+		if ((ret = l4shmc_get_chunk(&priv->shmcarea, myname,
+		                            &priv->rx_chunk))) {
+			printk(KERN_WARNING "%s: Can't attach to existing chunk '%s': %d",
+			       dev->name, myname, ret);
+			return ret;
+		}
+	}
 
-	if (length == 0)
-		return 0;
+	/*
+	 * The chunk ready / chunk cleared flag is abused as ifup/ifdown flag:
+	 * cleared == consumed  -> if down
+	 * ready                -> if up
+	 */
+	l4shmc_chunk_consumed(&priv->rx_chunk);
 
-	// copy chunk into the ring
-	chhead = (struct chunk_head *)l4shmc_chunk_ptr(&priv->tx_chunk);
+	if ((ret = l4shmc_add_signal(&priv->shmcarea, myname, &priv->rx_sig)))
+		return ret;
 
-	offs = chhead->next_offs_to_write;
+	if ((ret = l4shmc_connect_chunk_signal(&priv->rx_chunk, &priv->rx_sig)))
+		return ret;
 
-	rph = (struct ring_chunk_head *)(priv->tx_ring_start + offs);
+	return 0;
+}
 
-	BUILD_BUG_ON(sizeof(struct ring_chunk_head) & (sizeof(struct ring_chunk_head) - 1));
+static int init_dev_other(struct net_device *dev)
+{
+	struct l4x_l4shmc_priv *priv = netdev_priv(dev);
+	char myname[15];
+	char othername[15];
+	const char *ptr;
+	long offs = 0;
+	int ret;
+	L4XV_V(f);
 
-	nextoffs = (offs + length + sizeof(struct ring_chunk_head) + sizeof(struct ring_chunk_head) - 1)
-	           & ~(sizeof(struct ring_chunk_head) - 1);
+	othername[0] = '\0';
+	snprintf(myname, sizeof(myname), "%pm", dev->dev_addr);
+	L4XV_L(f);
+	while ((offs = l4shmc_iterate_chunk(&priv->shmcarea, &ptr, offs)) > 0) {
+		if (strncmp(ptr, myname, sizeof(myname)) != 0) {
+			strncpy(othername, ptr, sizeof(othername));
+			if (othername[sizeof(othername)-1] != '\0') {
+				/* name too long or invalid */
+				L4XV_U(f);
+				return -L4_EIO;
+			}
+		}
+	}
+	if (offs < 0) {
+		L4XV_U(f);
+		return offs;
+	}
+	if (!othername[0]) {
+		L4XV_U(f);
+		return -L4_EAGAIN;
+	}
 
-	r = chhead->next_offs_to_read;
-	if (r <= offs)
-		r += priv->tx_ring_size;
-	if (nextoffs >= r) {
-		chhead->writer_blocked = 1;
-		netif_stop_queue(netdev);
-		return 1;
+	if ((ret = l4shmc_get_chunk(&priv->shmcarea, othername,
+				    &priv->tx_chunk))) {
+		L4XV_U(f);
+		return ret;
 	}
 
-	nextoffs %= priv->tx_ring_size;
+	if ((ret = l4shmc_get_signal_to(&priv->shmcarea, othername,
+					WAIT_TIMEOUT, &priv->tx_sig))) {
+		L4XV_U(f);
+		return ret;
+	}
 
-	offs += sizeof(struct ring_chunk_head);
-	offs %= priv->tx_ring_size;
+	l4shm_buf_init(&priv->sb, l4shmc_chunk_ptr(&priv->rx_chunk),
+				l4shmc_chunk_capacity(&priv->rx_chunk),
+				l4shmc_chunk_ptr(&priv->tx_chunk),
+				l4shmc_chunk_capacity(&priv->tx_chunk));
 
-	if (offs + length > priv->tx_ring_size)
-		l = priv->tx_ring_size - offs;
-	else
-		l = length;
+	priv->remote_attached = 1;
+	l4shmc_trigger(&priv->tx_sig);
+	L4XV_U(f);
+	printk(KERN_INFO "%s: L4ShmNet established, with %pM, IRQ %d\n",
+	       dev->name, dev->dev_addr, dev->irq);
+	return 0;
+}
 
-	memcpy(priv->tx_ring_start + offs, (char *)skb->data, l);
-	if (l != length)
-		memcpy(priv->tx_ring_start, (char *)skb->data + l, length - l);
+static void update_carrier(struct net_device *dev)
+{
+	struct l4x_l4shmc_priv *priv = netdev_priv(dev);
+	int link_up = 0;
+
+	if (priv->remote_attached &&
+	    l4shmc_is_chunk_ready(&priv->tx_chunk) &&
+	    l4shmc_is_chunk_ready(&priv->rx_chunk))
+		link_up = 1;
+	if (priv->link_up == link_up)
+		return;
+	priv->link_up = link_up;
+	if (link_up) {
+		netif_carrier_on(dev);
+		netif_wake_queue(dev);
+	}
+	else {
+		netif_carrier_off(dev);
+		netif_stop_queue(dev);
+	}
+}
+
+static int l4x_l4shmc_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
+	int ret;
+	L4XV_V(f);
+
+	ret = l4shm_buf_tx(&priv->sb, skb->data, skb->len);
+	if (ret == -L4_EAGAIN) {
+		netif_stop_queue(netdev);
+		return 1;
+	}
+	/* XXX: handle other l4shm_buf_tx errors */
 
-	// now write to shm
-	rph->size = length;
-	rph = (struct ring_chunk_head *)(priv->tx_ring_start + nextoffs);
-	rph->size = 0;
-	chhead->next_offs_to_write = nextoffs;
 	wmb();
 
 	L4XV_L(f);
 	l4shmc_trigger(&priv->tx_sig);
 	L4XV_U(f);
 
+
 	netdev->trans_start = jiffies;
 	priv->net_stats.tx_packets++;
 	priv->net_stats.tx_bytes += skb->len;
@@ -149,8 +252,14 @@ static irqreturn_t l4x_l4shmc_interrupt(int irq, void *dev_id)
 	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
 	struct sk_buff *skb;
 	struct chunk_head *chhead;
-	struct ring_chunk_head *rph;
-	unsigned long offs;
+	unsigned long len;
+	int ret;
+
+	if (!priv->remote_attached)
+		init_dev_other(netdev);
+
+	update_carrier(netdev);
+	/* XXX: return if link down? */
 
 	chhead = (struct chunk_head *)l4shmc_chunk_ptr(&priv->tx_chunk);
 	if (chhead->writer_blocked) {
@@ -158,52 +267,24 @@ static irqreturn_t l4x_l4shmc_interrupt(int irq, void *dev_id)
 		netif_wake_queue(netdev);
 	}
 
-	chhead = (struct chunk_head *)l4shmc_chunk_ptr(&priv->rx_chunk);
-	offs = chhead->next_offs_to_read;
-	while (chhead->next_offs_to_read != chhead->next_offs_to_write) {
-		unsigned long l;
+	while ((len = l4shm_buf_rx_len(&priv->sb)) > 0) {
 		char *p;
 
-		rph = (struct ring_chunk_head *)(priv->rx_ring_start + offs);
-
-		if (!rph->size) {
-			// this check alone is not sufficient and with the
-			// chhead->next_offs_to_read != chhead->next_offs_to_write
-			// check above, this is redundant
-			break;
-		}
-
-		skb = dev_alloc_skb(rph->size);
+		/* NET_IP_ALIGN is non-zero on some architectures */
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
 
 		if (unlikely(!skb)) {
 			printk(KERN_WARNING "%s: dropping packet (%ld).\n",
-			       netdev->name, rph->size);
+			       netdev->name, len);
 			priv->net_stats.rx_dropped++;
 			break;
 		}
 
 		skb->dev = netdev;
-
-		offs += sizeof(struct ring_chunk_head);
-		offs %= priv->rx_ring_size;
-
-		if (offs + rph->size > priv->rx_ring_size)
-			l = priv->rx_ring_size - offs;
-		else
-			l = rph->size;
-
 		skb_reserve(skb, NET_IP_ALIGN);
-		p = skb_put(skb, rph->size);
-		memcpy(p, priv->rx_ring_start + offs, l);
-		if (l != rph->size)
-			memcpy(p + l, priv->rx_ring_start, rph->size - l);
-
-	        offs = (offs + rph->size + sizeof(struct ring_chunk_head) - 1)
-	               & ~(sizeof(struct ring_chunk_head) - 1);
-		offs %= priv->rx_ring_size;
-		chhead->next_offs_to_read = offs;
-		rph->size = 0;
-
+		p = skb_put(skb, len);
+		ret = l4shm_buf_rx(&priv->sb, p, len);
+		/* XXX: check errors */
 		skb->protocol = eth_type_trans(skb, netdev);
 		netif_rx(skb);
 
@@ -212,19 +293,22 @@ static irqreturn_t l4x_l4shmc_interrupt(int irq, void *dev_id)
 		priv->net_stats.rx_packets++;
 	}
 
-	if (chhead->writer_blocked) {
+	if (len == -L4_EAGAIN) {
 		L4XV_V(f);
 		L4XV_L(f);
 		l4shmc_trigger(&priv->tx_sig);
 		L4XV_U(f);
 	}
+	/* XXX: handle other errors */
 
 	return IRQ_HANDLED;
 }
 
 static int l4x_l4shmc_open(struct net_device *netdev)
 {
+	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
 	int err;
+	L4XV_V(f);
 
 	netif_carrier_off(netdev);
 
@@ -236,18 +320,26 @@ static int l4x_l4shmc_open(struct net_device *netdev)
 		return err;
 	}
 
-
-	netif_carrier_on(netdev);
-	netif_wake_queue(netdev);
+	l4shmc_chunk_ready(&priv->rx_chunk, 0);
+	L4XV_L(f);
+	l4shmc_trigger(&priv->tx_sig);
+	L4XV_U(f);
+	update_carrier(netdev);
 
 	return 0;
 }
 
 static int l4x_l4shmc_close(struct net_device *netdev)
 {
+	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
+	L4XV_V(f);
+
 	free_irq(netdev->irq, netdev);
-	netif_stop_queue(netdev);
-	netif_carrier_off(netdev);
+	l4shmc_chunk_consumed(&priv->rx_chunk);
+	L4XV_L(f);
+	l4shmc_trigger(&priv->tx_sig);
+	L4XV_U(f);
+	update_carrier(netdev);
 
 	return 0;
 }
@@ -255,8 +347,10 @@ static int l4x_l4shmc_close(struct net_device *netdev)
 static int l4x_l4shmc_change_mtu(struct net_device *netdev, int new_mtu)
 {
 	struct l4x_l4shmc_priv *priv = netdev_priv(netdev);
+	int max_mtu = min(l4shmc_chunk_capacity(&priv->rx_chunk),
+			  l4shmc_chunk_capacity(&priv->tx_chunk)) - 100;
 
-	if (new_mtu > chunk_size(&priv->shmcarea) - 100)
+	if (new_mtu > max_mtu)
 		return -EINVAL;
 
 	netdev->mtu = new_mtu;
@@ -272,13 +366,13 @@ static const struct net_device_ops l4shmnet_netdev_ops = {
 
 };
 
-static int __init l4x_l4shmnet_init_dev(int num, const char *name)
+static int __init l4x_l4shmnet_init_dev(int num)
 {
 	struct l4x_l4shmc_priv *priv;
 	struct net_device *dev = NULL;
 	struct l4x_l4shmc_netdev *nd = NULL;
-	struct chunk_head *ch;
-	int err;
+	const char *name = devs_to_add_name[num];
+	int err, ret;
 	L4XV_V(f);
 
 	if (shmsize < PAGE_SIZE)
@@ -288,16 +382,24 @@ static int __init l4x_l4shmnet_init_dev(int num, const char *name)
 		return -ENOMEM;
 
 	dev->netdev_ops = &l4shmnet_netdev_ops,
+	dev->dev_addr[0] = 0x52;
+	dev->dev_addr[1] = 0x54;
+	dev->dev_addr[2] = 0x00;
+	dev->dev_addr[3] = 0xb0;
+	dev->dev_addr[4] = 0xcf;
+	dev->dev_addr[5] = devs_to_add_macpart[num];
+
 	priv = netdev_priv(dev);
+	priv->num = num;
+	priv->link_up = 0;
+	priv->remote_attached = 0;
 
-	printk("%s: Requesting, role %s, Shmsize %d Kbytes\n",
-	       name,
-               devs_create[num] ? "Creator" : "User", shmsize >> 10);
+	printk("%s: Requesting, Shmsize %d Kbytes\n", name, shmsize >> 10);
 
 	L4XV_L(f);
 	err = -ENOMEM;
-	if (devs_create[num]) {
-		if (l4shmc_create(name, shmsize))
+	if ((ret = l4shmc_create(name, shmsize)) < 0) {
+		if (ret != -L4_EEXIST)
 			goto err_out_free_dev_unlock;
 	}
 
@@ -305,55 +407,14 @@ static int __init l4x_l4shmnet_init_dev(int num, const char *name)
 	if (l4shmc_attach_to(name, WAIT_TIMEOUT, &priv->shmcarea))
 		goto err_out_free_dev_unlock;
 
-	if (l4shmc_add_chunk(&priv->shmcarea, devs_create[num] ? "joe" : "bob",
-	                     chunk_size(&priv->shmcarea), &priv->tx_chunk))
-		goto err_out_free_dev_unlock;
-
-	if (l4shmc_add_signal(&priv->shmcarea, devs_create[num] ? "joe" : "bob",
-	                      &priv->tx_sig))
-		goto err_out_free_dev_unlock;
-
-	if (l4shmc_connect_chunk_signal(&priv->tx_chunk, &priv->tx_sig))
-		goto err_out_free_dev_unlock;
-
-	/* Now get the receiving side */
-	if (l4shmc_get_chunk_to(&priv->shmcarea, devs_create[num] ? "bob" : "joe",
-	                        WAIT_TIMEOUT, &priv->rx_chunk)) {
-		printk("%s: Did not find other side\n", name);
+	ret = init_dev_self(dev);
+	if (ret < 0) {
+		/* XXX: convert error code */
 		goto err_out_free_dev_unlock;
 	}
 
-	if (l4shmc_get_signal_to(&priv->shmcarea, devs_create[num] ? "bob" : "joe",
-	                         WAIT_TIMEOUT, &priv->rx_sig)) {
-		printk("%s: Could not get signal\n", name);
-		goto err_out_free_dev_unlock;
-	}
-	if (l4shmc_connect_chunk_signal(&priv->rx_chunk, &priv->rx_sig))
-		goto err_out_free_dev_unlock;
 	L4XV_U(f);
 
-	ch = (struct chunk_head *)l4shmc_chunk_ptr(&priv->tx_chunk);
-	ch->next_offs_to_write = 0;
-	ch->next_offs_to_read  = 0;
-	ch->writer_blocked     = 0;
-
-	priv->tx_ring_size = l4shmc_chunk_capacity(&priv->tx_chunk)
-	                       - sizeof(struct chunk_head);
-	priv->rx_ring_size = l4shmc_chunk_capacity(&priv->rx_chunk)
-	                       - sizeof(struct chunk_head);
-
-	priv->tx_ring_start = (char *)l4shmc_chunk_ptr(&priv->tx_chunk)
-	                       + sizeof(struct chunk_head);
-	priv->rx_ring_start = (char *)l4shmc_chunk_ptr(&priv->rx_chunk)
-	                       + sizeof(struct chunk_head);
-
-	dev->dev_addr[0] = 0x52;
-	dev->dev_addr[1] = 0x54;
-	dev->dev_addr[2] = 0x00;
-	dev->dev_addr[3] = 0xb0;
-	dev->dev_addr[4] = 0xcf;
-	dev->dev_addr[5] = devs_to_add_macpart[num];
-
 	if ((dev->irq = l4x_register_irq(l4shmc_signal_cap(&priv->rx_sig))) < 0) {
 		printk("Failed to get virq\n");
 		goto err_out_free_dev;
@@ -375,8 +436,11 @@ static int __init l4x_l4shmnet_init_dev(int num, const char *name)
 	nd->dev = dev;
 	list_add(&nd->list, &l4x_l4shmnet_netdevices);
 
-	printk(KERN_INFO "%s: L4ShmNet established, with %pM, IRQ %d\n",
-	                 dev->name, dev->dev_addr, dev->irq);
+	ret = init_dev_other(dev);
+	if (ret < 0 && ret != -L4_EAGAIN) {
+		/* XXX: convert error code */
+		goto err_out_free_dev;
+	}
 
 	return 0;
 
@@ -395,7 +459,7 @@ static int __init l4x_l4shmnet_init(void)
 
 	for (i = 0; i < devs_to_add_pos; ++i)
 		if (*devs_to_add_name[i]
-		    && !l4x_l4shmnet_init_dev(i, devs_to_add_name[i]))
+		    && !l4x_l4shmnet_init_dev(i))
 			ret = 0;
 	return ret;
 }
@@ -437,13 +501,17 @@ static int l4x_l4shmnet_setup(const char *val, struct kernel_param *kp)
 	if (c) {
 		l = c - val + 1;
 		do {
-			if (!strncmp(c + 1, "create", 6))
-				devs_create[devs_to_add_pos] = 1;
-			else if (!strncmp(c + 1, "macpart=", 8)) {
+			c++;
+			if (!strncmp(c, "macpart=", 8)) {
 				devs_to_add_macpart[devs_to_add_pos]
-				  = simple_strtoul(c + 9, NULL, 0);
+				  = simple_strtoul(c + 8, NULL, 0);
+			}
+			else {
+				char *end = strchr(c, ',');
+				printk("l4shmnet: unknown argument: %*s",
+					end ? end - c : strlen(c), c);
 			}
-		} while ((c = strchr(c + 1, ',')));
+		} while ((c = strchr(c, ',')));
 	}
 	strlcpy(devs_to_add_name[devs_to_add_pos], val, l);
 	devs_to_add_pos++;
@@ -451,7 +519,7 @@ static int l4x_l4shmnet_setup(const char *val, struct kernel_param *kp)
 }
 
 module_param_call(add, l4x_l4shmnet_setup, NULL, NULL, 0200);
-MODULE_PARM_DESC(add, "Use l4shmnet.add=name,macpart[,create] to add a device, name queried in namespace");
+MODULE_PARM_DESC(add, "Use l4shmnet.add=name,macpart=xx to add a device, name queried in namespace");
 
 module_param(shmsize, int, 0);
 MODULE_PARM_DESC(shmsize, "Size of the shared memory area");
shmc.diff (text/x-diff, 30.9 KB)
diff --git a/src/l4/pkg/shmc/include/internal.h b/src/l4/pkg/shmc/include/internal.h
index c4cb4bf..6c7faeb 100644
--- a/src/l4/pkg/shmc/include/internal.h
+++ b/src/l4/pkg/shmc/include/internal.h
@@ -24,6 +24,23 @@ l4shmc_attach(const char *shmc_name, l4shmc_area_t *shmarea)
   return l4shmc_attach_to(shmc_name, 0, shmarea);
 }
 
+L4_CV L4_INLINE void l4shmc_membarrier(void)
+{
+  /*
+   * l4_barrier() is only a compiler memory barrier, but we need a hardware
+   * memory barrier (at least if Fiasco runs on SMP)
+   */
+#if defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_5TE__)
+  /*
+   * Early ARM arches don't support SMP and, depending on the version,
+   * gcc either does not have __sync_synchronize for them or it requires
+   * special kernel support.
+   */
+  l4_barrier();
+#else
+  __sync_synchronize();
+#endif
+}
 
 L4_CV L4_INLINE long
 l4shmc_wait_any(l4shmc_signal_t **p)
@@ -91,7 +108,7 @@ L4_CV L4_INLINE long
 l4shmc_chunk_ready(l4shmc_chunk_t *chunk, l4_umword_t size)
 {
   chunk->_chunk->_size = size;
-  asm volatile("" : : : "memory");
+  l4shmc_membarrier();
   chunk->_chunk->_status = L4SHMC_CHUNK_READY;
   return L4_EOK;
 }
@@ -128,16 +145,19 @@ l4shmc_signal_cap(l4shmc_signal_t *signal)
   return signal->_sigcap;
 }
 
-L4_CV L4_INLINE l4_umword_t
+L4_CV L4_INLINE long
 l4shmc_chunk_size(l4shmc_chunk_t *p)
 {
-  return p->_chunk->_size;
+  l4_umword_t s = p->_chunk->_size;
+  if (s > p->_capacity)
+    return -L4_EIO;
+  return s;
 }
 
-L4_CV L4_INLINE l4_umword_t
+L4_CV L4_INLINE long
 l4shmc_chunk_capacity(l4shmc_chunk_t *p)
 {
-  return p->_chunk->_capacity;
+  return p->_capacity;
 }
 
 L4_CV L4_INLINE long
@@ -152,7 +172,6 @@ l4shmc_chunk_try_to_take(l4shmc_chunk_t *chunk)
 L4_CV L4_INLINE long
 l4shmc_chunk_consumed(l4shmc_chunk_t *chunk)
 {
-  asm volatile("" : : : "memory");
   chunk->_chunk->_status = L4SHMC_CHUNK_CLEAR;
   return L4_EOK;
 }
diff --git a/src/l4/pkg/shmc/include/shmbuf.h b/src/l4/pkg/shmc/include/shmbuf.h
new file mode 100644
index 0000000..c1b636e
--- /dev/null
+++ b/src/l4/pkg/shmc/include/shmbuf.h
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2011 Stefan Fritsch <[email protected]>
+ *                    Christian Ehrhardt <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef L4SHMC_SHMBUF_H
+#define L4SHMC_SHMBUF_H
+
+#include <l4/shmc/shmc.h>
+#include <l4/sys/err.h>
+#include <l4/sys/compiler.h>
+
+EXTERN_C_BEGIN
+
+/* sizeof(struct l4shm_buf_pkt_head) must be power of two */
+struct l4shm_buf_pkt_head
+{
+  unsigned long size;
+};
+
+struct l4shm_buf_chunk_head
+{
+  /** end of ring content */
+  volatile unsigned long next_offs_to_write;
+  /** start of ring content */
+  volatile unsigned long next_offs_to_read;
+  /** ring buffer full */
+  volatile unsigned long writer_blocked;
+  /** The packet buffers. */
+  volatile struct l4shm_buf_pkt_head pkg[0];
+};
+
+/**
+ * Return the size of the largest packet that currently fits into
+ * the given chunk.
+ * @param chunk The chunk.
+ * @param chunksize The size of the data area of the chunk (maintained
+ *     outside of the shared memory area).
+ * It is the callers responsibility to ensure that it is the sender of
+ * this chunk.
+ */
+L4_INLINE unsigned long l4shm_buf_chunk_tx_free(struct l4shm_buf_chunk_head *chunk,
+    unsigned long chunksize)
+{
+  unsigned long roff = chunk->next_offs_to_read;
+  unsigned long woff = chunk->next_offs_to_write;
+  unsigned long space;
+
+  if (woff >= chunksize || roff >= chunksize)
+    return 0;
+
+  if (woff < roff)
+    space = roff - woff;
+  else
+    space = chunksize - (woff - roff);
+  if (space < 2 * sizeof(struct l4shm_buf_pkt_head))
+    return 0;
+  return space - 2 * sizeof(struct l4shm_buf_pkt_head);
+}
+
+/** align v to power of 2 boundary */
+L4_INLINE l4_umword_t l4shmc_align(l4_umword_t v, l4_umword_t boundary)
+{
+  return (v + boundary - 1 ) & ~(boundary - 1);
+}
+
+L4_INLINE volatile struct l4shm_buf_pkt_head *l4shmc_align_ptr_ph(volatile struct l4shm_buf_pkt_head *v)
+{
+  return (struct l4shm_buf_pkt_head *)l4shmc_align((l4_umword_t)v, sizeof(struct l4shm_buf_pkt_head));
+}
+
+L4_INLINE l4_umword_t l4shmc_align_off_ph(l4_umword_t v)
+{
+  return l4shmc_align(v, sizeof(struct l4shm_buf_pkt_head));
+}
+
+/**
+ * Initialize a chunk.
+ * Note that the entire chunk structure lives in shared memory.
+ * @param chunk The chunk structure.
+ * @param chunksize The size of the chunk including the l4shm_buf_chunk_head
+ *     structure. This value is not maintained inside the chunk head
+ *     but used to check alignment requirements.
+ * @return True if initialization was successful, a negative error code
+ *     if initialization failed.
+ */
+L4_INLINE int l4shm_buf_chunk_init(struct l4shm_buf_chunk_head *chunk,
+				   unsigned long chunksize)
+{
+  static_assert(0 == ( sizeof(struct l4shm_buf_pkt_head) &
+		       (sizeof(struct l4shm_buf_pkt_head)-1) ),
+		"sizeof(struct l4shm_buf_pkt_head) not power of 2");
+
+  if (chunk->pkg != l4shmc_align_ptr_ph(chunk->pkg))
+    return -L4_EINVAL;
+  chunksize -= sizeof(struct l4shm_buf_chunk_head);
+  if (chunksize != l4shmc_align_off_ph(chunksize))
+    return -L4_EINVAL;
+  chunk->pkg[0].size = 0;
+  chunk->next_offs_to_write = 0,
+	 chunk->next_offs_to_read = 0;
+  chunk->writer_blocked = 0;
+  return 0;
+}
+
+/**
+ * Add part of a packet to the given chunk. The data is only copied
+ * into the area of the chunk reserved for the sender. It is not made
+ * available for the receiver. Use l4shm_buf_chunk_tx_complete for this.
+ * It is the callers responsibility to ensure that it is the sender
+ * on this chunk.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @param buf The packet data.
+ * @param poffset The offset of this chunk within the packet. The caller must
+ *     make sure that it only commits complete packets.
+ * @param len The length of the packet data.
+ * @return Zero if the packet was added to the chunk, a negative error
+ *     code otherwise. In particular -L4_EAGAIN means that insufficient
+ *     space was available in the ring.
+ */
+L4_INLINE int l4shm_buf_chunk_tx_part(struct l4shm_buf_chunk_head *ch,
+				      unsigned long chunksize, const char *buf,
+				      unsigned long poffset, unsigned long len)
+{
+  unsigned long woffset, nextoffset, r, part_len, totallen;
+  volatile struct l4shm_buf_pkt_head *ph;
+  int blocked = 0;
+
+  if (len == 0)
+    return 0;
+  if (poffset > chunksize || len > chunksize)
+    return -L4_ENOMEM;
+  totallen = poffset + len;
+  if (totallen > chunksize - 2*sizeof(struct l4shm_buf_pkt_head))
+    return -L4_ENOMEM;
+
+retry:
+  l4shmc_membarrier();
+  woffset = ch->next_offs_to_write;
+  if (woffset >= chunksize || woffset != l4shmc_align_off_ph(woffset))
+    return -L4_EIO;
+  ph = ch->pkg + (woffset / sizeof(struct l4shm_buf_pkt_head));
+
+  nextoffset = l4shmc_align_off_ph(woffset + totallen
+				   + sizeof(struct l4shm_buf_pkt_head));
+
+  r = ch->next_offs_to_read;
+  if (r >= chunksize)
+    return -L4_EIO;
+  if (r <= woffset)
+    r += chunksize;
+
+  /* Don't use all space, L4Linux needs an additional '0' chunk head after
+   * the chunk. Therefore we need an additional struct l4shm_buf_pkt_head.
+   */
+  if (nextoffset + sizeof(struct l4shm_buf_pkt_head) > r)
+    {
+      /*
+       * If there is insufficient space set writer_blocked and
+       * retry. This is neccessary to avoid a race where we set
+       * writer blocked after the peer emptied the buffer. We don't
+       * set writer_blocked in the first try to avoid spurious interrupts
+       * triggered by the reader due to writer_blocked.
+       */
+      if (blocked)
+	return  -L4_EAGAIN;
+      ch->writer_blocked = 1;
+      blocked = 1;
+      goto retry;
+    }
+
+  ch->writer_blocked = 0;
+
+  woffset += sizeof(struct l4shm_buf_pkt_head) + poffset;
+  woffset %= chunksize;
+
+  if (woffset + len > chunksize)
+    part_len = chunksize - woffset;
+  else
+    part_len = len;
+
+  memcpy(((unsigned char *)ch->pkg)+woffset, buf, part_len);
+  if (len != part_len)
+    memcpy((void*)ch->pkg, buf + part_len, len - part_len);
+
+  return 0;
+}
+
+/**
+ * Complete the transmission of a packet. This function fills in the
+ * packet head in the shm buffer. The packet data must already be present.
+ * Use l4shm_buf_chunk_tx_part to copy packet data.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @param pkglen The total length of the packet.
+ * @return Zero in case of success or a negative error code.
+ */
+L4_INLINE int l4shm_buf_chunk_tx_complete(struct l4shm_buf_chunk_head *ch,
+    unsigned long chunksize, size_t pkglen)
+{
+  unsigned long offset, nextoffset, r;
+  volatile struct l4shm_buf_pkt_head *ph, *next_ph;
+
+  if (pkglen == 0)
+    return 0;
+  if (pkglen > chunksize - 2*sizeof(struct l4shm_buf_pkt_head))
+    return -L4_ENOMEM;
+
+  offset = ch->next_offs_to_write;
+  if (offset >= chunksize || offset != l4shmc_align_off_ph(offset))
+    return -L4_EIO;
+  ph = ch->pkg + (offset / sizeof(struct l4shm_buf_pkt_head));
+
+  nextoffset = l4shmc_align_off_ph(offset + pkglen + sizeof(struct l4shm_buf_pkt_head));
+
+  r = ch->next_offs_to_read;
+  if (r >= chunksize)
+    return -L4_EIO;
+  if (r <= offset)
+    r += chunksize;
+
+  /* Don't use all space, L4Linux needs an additional '0' chunk head after
+   * the chunk. Therefore we need an additional struct l4shm_buf_pkt_head.
+   */
+  if (nextoffset + sizeof(struct l4shm_buf_pkt_head) > r)
+    return  -L4_EIO;
+
+  nextoffset %= chunksize;
+  /* For L4Linux compatibility */
+  next_ph = ch->pkg + (nextoffset / sizeof(struct l4shm_buf_pkt_head));
+  next_ph->size = 0;
+
+  l4shmc_membarrier();
+
+  ph->size = pkglen;
+  ch->next_offs_to_write = nextoffset;
+  return 0;
+}
+
+/**
+ * Add a packet to the given chunk.
+ * It is the callers responsibility to ensure that it is the sender
+ * on this chunk.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @param buf The packet data.
+ * @param size The length of the packet data.
+ * @return Zero if the packet was added to the chunk, a negative error
+ *     code otherwise. In particular -L4_EAGAIN means that insufficient
+ *     space was available in the ring.
+ * It is the callers responsibility to wake up the receiver after adding
+ * packets or when detecting insufficient space in the buffer.
+ */
+L4_INLINE int l4shm_buf_chunk_tx(struct l4shm_buf_chunk_head *ch,
+				 unsigned long chunksize,
+				 const char *buf, unsigned long pkt_size)
+{
+  int ret = l4shm_buf_chunk_tx_part(ch, chunksize, buf, 0, pkt_size);
+  if (ret < 0)
+    return ret;
+  return l4shm_buf_chunk_tx_complete(ch, chunksize, pkt_size);
+}
+
+
+/**
+ * Return the length of the next packet in the chunk.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *    excluding the leading l4shm_buf_chunk_head structure.
+ * @return Zero if the chunk is empty, the length of the first
+ *    packet in the chunk or a negative value in case of an error.
+ */
+L4_INLINE int l4shm_buf_chunk_rx_len(struct l4shm_buf_chunk_head *ch,
+				     unsigned long chunksize)
+{
+  unsigned long offset = ch->next_offs_to_read;
+  unsigned long woffset = ch->next_offs_to_write;
+  long space = woffset - offset;
+  unsigned long pkt_size;
+  volatile struct l4shm_buf_pkt_head *ph;
+
+  if (offset >= chunksize)
+    return -L4_EIO;
+  if (woffset >= chunksize)
+    return -L4_EIO;
+  if (space == 0)
+    return 0;
+  if (space < 0)
+    space += chunksize;
+  if (space < (int)sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  ph = ch->pkg + (offset / sizeof(struct l4shm_buf_pkt_head));
+  pkt_size = ph->size;
+  if (pkt_size > (unsigned long)space - sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  return pkt_size;
+}
+
+/**
+ * Drop the first packet in the chunk. The packet must have non-zero length.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @return Zero if the packet could be dropped, a negative value in case
+ *     of an error.
+ */
+L4_INLINE int l4shm_buf_chunk_rx_drop(struct l4shm_buf_chunk_head *ch,
+				      unsigned long chunksize)
+{
+  unsigned long offset = ch->next_offs_to_read;
+  unsigned long woffset = ch->next_offs_to_write;
+  long space = woffset - offset;
+  unsigned long pkt_size;
+  volatile struct l4shm_buf_pkt_head *ph;
+
+  if (offset >= chunksize)
+    return -L4_EIO;
+  if (woffset >= chunksize)
+    return -L4_EIO;
+  if (space == 0)
+    return -L4_ENOENT;
+  if (space < 0)
+    space += chunksize;
+  if (space < (int)sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  ph = ch->pkg + (offset / sizeof(struct l4shm_buf_pkt_head));
+  pkt_size = ph->size;
+  if (pkt_size > (unsigned long)space - sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  offset = l4shmc_align_off_ph(offset + sizeof(struct l4shm_buf_pkt_head) + pkt_size);
+  offset %= chunksize;
+  ch->next_offs_to_read = offset;
+  l4shmc_membarrier();
+
+  return 0;
+}
+
+/**
+ * Copy part of a packet from the shm chunk to a buffer. The caller
+ * must make sure that the packet contains enough data and that the
+ * buffer is long enough to receive the data.
+ * @param ch The chunk. The data in the chunk is not modified!
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @param poffset The offset of the part to copy within the packet.
+ *     It is an error if this offset is beyond the length of the packet.
+ * @param len The amount to copy. It is an error if the packet is shorter
+ *     than offset+len bytes.
+ * @param buf The target buffer. The caller must make sure that the buffer
+ *     can hold len bytes.
+ * @return Zero or a negative error code.
+ */
+L4_INLINE int l4shm_buf_chunk_rx_part(const struct l4shm_buf_chunk_head *ch,
+				      unsigned long chunksize,
+				      unsigned long poffset,
+				      unsigned long len, char *buf)
+{
+  unsigned long roffset = ch->next_offs_to_read;
+  unsigned long woffset = ch->next_offs_to_write;
+  long space = woffset - roffset;
+  volatile const struct l4shm_buf_pkt_head *ph;
+  unsigned long part_len, pkt_size;
+
+  if (roffset >= chunksize)
+    return -L4_EIO;
+  if (woffset >= chunksize)
+    return -L4_EIO;
+  if (space < 0)
+    space += chunksize;
+  if (space < (int)sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  ph = ch->pkg + (roffset / sizeof(struct l4shm_buf_pkt_head));
+  pkt_size = ph->size;
+  if (pkt_size > (unsigned long)space - sizeof(struct l4shm_buf_pkt_head))
+    return -L4_EIO;
+  /* Backward compatibility (pkt_size == 0 means no more packets) */
+  if (pkt_size == 0)
+    return -L4_ENOENT;
+  if (poffset >= pkt_size || len > pkt_size || poffset + len > pkt_size)
+    return -L4_ENOENT;
+  roffset += poffset + sizeof(struct l4shm_buf_pkt_head);
+  roffset %= chunksize;
+
+  if (roffset + len > chunksize)
+    part_len = chunksize - roffset;
+  else
+    part_len = len;
+  memcpy(buf, ((unsigned char *)ch->pkg)+roffset, part_len);
+  if (part_len != len)
+    memcpy(buf + part_len, (unsigned char *)ch->pkg, len - part_len);
+
+  return 0;
+}
+
+/**
+ * Remove a packet from the given chunk.
+ * It is the callers responsibility to ensure that it is the receiver
+ * on this chunk.
+ * @param ch The chunk.
+ * @param chunksize The size of the payload data in the chunk, i.e.
+ *     excluding the leading l4shm_buf_chunk_head structure.
+ * @param buf The packet buffer.
+ * @param size The length of the packet buffer.
+ * @return The size of the received packet, zero if no packet was available
+ *    and a negative error code otherwise.
+ * It is the callers responsibility to wake up a potentially blocked
+ * sender after removing data from the buffer.
+ */
+L4_INLINE int l4shm_buf_chunk_rx(struct l4shm_buf_chunk_head *ch,
+				 unsigned long chunksize,
+				 char *buf, unsigned long buf_size)
+{
+  int ret = l4shm_buf_chunk_rx_len(ch, chunksize);
+  if (ret <= 0)
+    return ret;
+  if (buf_size < ret)
+    return -L4_ENOMEM;
+  ret = l4shm_buf_chunk_rx_part(ch, chunksize, 0, ret, buf);
+  if (ret < 0)
+    return ret;
+  return l4shm_buf_chunk_rx_drop(ch, chunksize);
+}
+
+/*
+ * Various wrappers for l4shm_buf_chunk_* functions using a single l4shm_buf struct
+ */
+
+struct l4shm_buf
+{
+  struct l4shm_buf_chunk_head *tx_head;
+  struct l4shm_buf_chunk_head *rx_head;
+  unsigned long tx_ring_size;
+  unsigned long rx_ring_size;
+};
+
+/**
+ * Initialize an l4shm_buf structure with pre-allocated Rx/Tx rings
+ * @param sb A pre-allocated l4shm_buf structure to  initialize.
+ * @param rx_chunk The receive ring for this thread.
+ * @param rx_size The size of the receive ring including the l4shm_buf_chunk_head.
+ * @param tx_chunk The transmit ring for this thread.
+ * @param tx_size The size of the transmit ring including the l4shm_buf_chunk_head.
+ * @return True if successful, a negative error code if initialization
+ *     failed (normally due to bad alignment).
+ * This library will ensure that this thread only adds data to the tx
+ * ring and only removes data from the rx ring.
+ * It is possible to do initialization in two steps, by calling l4shm_buf_init()
+ * twice, once with rx_chunk == NULL and once with tx_chunk == NULL.
+ */
+L4_INLINE int l4shm_buf_init(struct l4shm_buf *sb, char *rx_chunk, unsigned long rx_size,
+			     char *tx_chunk, unsigned long tx_size)
+{
+  if (tx_chunk)
+    {
+      if (l4shm_buf_chunk_init((struct l4shm_buf_chunk_head *)tx_chunk, tx_size))
+	return -L4_EINVAL;
+      sb->tx_head = (struct l4shm_buf_chunk_head *)tx_chunk;
+      sb->tx_ring_size = tx_size - sizeof(struct l4shm_buf_chunk_head);
+    }
+  else
+    {
+      sb->tx_head = NULL;
+    }
+
+  if (rx_chunk)
+    {
+      if (l4shm_buf_chunk_init((struct l4shm_buf_chunk_head *)rx_chunk, rx_size))
+	return -L4_EINVAL;
+      sb->rx_head = (struct l4shm_buf_chunk_head *)rx_chunk;
+      sb->rx_ring_size = rx_size - sizeof(struct l4shm_buf_chunk_head);
+    }
+  else if (!tx_chunk)
+    {
+      return -L4_EINVAL;
+    }
+  else
+    {
+      sb->rx_head = NULL;
+    }
+
+  return 0;
+}
+
+L4_INLINE int l4shm_buf_rx_len(struct l4shm_buf *sb)
+{
+  return l4shm_buf_chunk_rx_len(sb->rx_head, sb->rx_ring_size);
+}
+
+L4_INLINE int l4shm_buf_rx_drop(struct l4shm_buf *sb)
+{
+  return l4shm_buf_chunk_rx_drop(sb->rx_head, sb->rx_ring_size);
+}
+
+L4_INLINE int l4shm_buf_rx_part(struct l4shm_buf *sb, unsigned long poffset,
+				unsigned long len, char *buf)
+{
+  return l4shm_buf_chunk_rx_part(sb->rx_head, sb->rx_ring_size, poffset, len, buf);
+}
+
+L4_INLINE unsigned long l4shm_buf_tx_free(struct l4shm_buf *sb)
+{
+  return l4shm_buf_chunk_tx_free(sb->tx_head, sb->tx_ring_size);
+}
+
+L4_INLINE int l4shm_buf_tx(struct l4shm_buf *sb, const char *buf,
+			   unsigned long pkt_size)
+{
+  return l4shm_buf_chunk_tx(sb->tx_head, sb->tx_ring_size, buf, pkt_size);
+}
+
+L4_INLINE int l4shm_buf_rx(struct l4shm_buf *sb, char *buf,
+			   unsigned long buf_size)
+{
+  return l4shm_buf_chunk_rx(sb->rx_head, sb->rx_ring_size, buf, buf_size);
+}
+
+
+L4_INLINE int l4shm_buf_tx_part(struct l4shm_buf *sb, const char *buf,
+				unsigned long poffset, unsigned long len)
+{
+  return l4shm_buf_chunk_tx_part(sb->tx_head, sb->tx_ring_size, buf, poffset, len);
+}
+
+L4_INLINE int l4shm_buf_tx_complete(struct l4shm_buf *sb, size_t pktlen)
+{
+  return l4shm_buf_chunk_tx_complete(sb->tx_head, sb->tx_ring_size, pktlen);
+}
+
+EXTERN_C_END
+
+#endif
diff --git a/src/l4/pkg/shmc/include/shmc.h b/src/l4/pkg/shmc/include/shmc.h
index 72f224b..d90e819 100644
--- a/src/l4/pkg/shmc/include/shmc.h
+++ b/src/l4/pkg/shmc/include/shmc.h
@@ -216,6 +216,20 @@ l4shmc_get_chunk_to(l4shmc_area_t *shmarea,
                     l4shmc_chunk_t *chunk);
 
 /**
+ * \brief Iterate over names of all existing chunks
+ * \ingroup api_l4shmc_chunk
+ *
+ * \param shmarea     Shared memory area.
+ * \param chunk_name  Where the name of the current chunk will be stored
+ * \param offs        0 to start iteration, return value of previous
+ *                    call to l4shmc_iterate_chunk() to get next chunk
+ * \return <0 on error, 0 if no more chunks, >0 iterator value for next call
+ */
+L4_CV long
+l4shmc_iterate_chunk(l4shmc_area_t *shmarea, const char **chunk_name,
+                     long offs);
+
+/**
  * \brief Attach to signal.
  * \ingroup api_l4shmc_signal
  *
@@ -468,7 +482,7 @@ l4shmc_chunk_ptr(l4shmc_chunk_t *chunk);
  * \param chunk Chunk.
  * \return 0 on success, <0 on error
  */
-L4_CV L4_INLINE l4_umword_t
+L4_CV L4_INLINE long
 l4shmc_chunk_size(l4shmc_chunk_t *chunk);
 
 /**
@@ -478,7 +492,7 @@ l4shmc_chunk_size(l4shmc_chunk_t *chunk);
  * \param chunk Chunk.
  * \return 0 on success, <0 on error
  */
-L4_CV L4_INLINE l4_umword_t
+L4_CV L4_INLINE long
 l4shmc_chunk_capacity(l4shmc_chunk_t *chunk);
 
 /**
@@ -522,6 +536,37 @@ l4shmc_check_magic(l4shmc_chunk_t *chunk);
 L4_CV L4_INLINE long
 l4shmc_area_size(l4shmc_area_t *shmarea);
 
+/**
+ * \brief Get free size of shared memory area. To get the max size to
+ * pass to l4shmc_add_chunk, substract l4shmc_chunk_overhead().
+ * \ingroup api_l4shm
+ *
+ * \param shmarea Shared memory area.
+ * \return <0 on error, otherwise: free capacity in the area.
+ *
+ */
+L4_CV long
+l4shmc_area_size_free(l4shmc_area_t *shmarea);
+
+/**
+ * \brief Get memory overhead per area that is not available for chunks
+ * \ingroup api_l4shm
+ *
+ * \return size of the overhead in bytes
+ */
+L4_CV long
+l4shmc_area_overhead(void);
+
+/**
+ * \brief Get memory overhead required in addition to the chunk capacity
+ * for adding one chunk
+ * \ingroup api_l4shm
+ *
+ * \return size of the overhead in bytes
+ */
+L4_CV long
+l4shmc_chunk_overhead(void);
+
 #include <l4/shmc/internal.h>
 
 __END_DECLS
diff --git a/src/l4/pkg/shmc/include/types.h b/src/l4/pkg/shmc/include/types.h
index f042b53..70150df 100644
--- a/src/l4/pkg/shmc/include/types.h
+++ b/src/l4/pkg/shmc/include/types.h
@@ -50,6 +50,7 @@ typedef struct {
   l4re_ds_t         _shm_ds;
   void             *_local_addr;
   char              _name[L4SHMC_NAME_STRINGLEN];
+  l4_umword_t       _size;
 } l4shmc_area_t;
 
 /* l4shmc_signal_t is local to one address space */
@@ -62,4 +63,5 @@ typedef struct {
   l4shmc_chunk_desc_t  *_chunk;
   l4shmc_area_t        *_shm;
   l4shmc_signal_t      *_sig;
+  l4_umword_t           _capacity;
 } l4shmc_chunk_t;
diff --git a/src/l4/pkg/shmc/lib/src/shmc.c b/src/l4/pkg/shmc/lib/src/shmc.c
index 82451e7..0ef7b87 100644
--- a/src/l4/pkg/shmc/lib/src/shmc.c
+++ b/src/l4/pkg/shmc/lib/src/shmc.c
@@ -36,6 +36,9 @@ enum {
   SHMAREA_LOCK_FREE, SHMAREA_LOCK_TAKEN,
 };
 
+enum {
+  MAX_SIZE = (~0UL) >> 1,
+};
 
 static inline l4shmc_chunk_desc_t *
 chunk_get(l4_addr_t o, void *shm_local_addr)
@@ -49,7 +52,10 @@ l4shmc_create(const char *shm_name, l4_umword_t shm_size)
   shared_mem_t *s;
   l4re_ds_t shm_ds = L4_INVALID_CAP;
   l4re_namespace_t shm_cap;
-  long r = -L4_ENOMEM;
+  long r;
+
+  if (shm_size > MAX_SIZE)
+    return -L4_ENOMEM;
 
   shm_cap = l4re_get_env_cap(shm_name);
   if (l4_is_invalid_cap(shm_cap))
@@ -66,6 +72,7 @@ l4shmc_create(const char *shm_name, l4_umword_t shm_size)
     goto out_shm_free_mem;
 
   s->_first_chunk = 0;
+  s->lock = SHMAREA_LOCK_FREE;
 
   r = l4re_ns_register_obj_srv(shm_cap, "shm", shm_ds, L4RE_NS_REGISTER_RW);
   l4re_rm_detach_unmap((l4_addr_t)s, L4RE_THIS_TASK_CAP);
@@ -90,6 +97,7 @@ l4shmc_attach_to(const char *shm_name, l4_umword_t timeout_ms,
 
   strncpy(shmarea->_name, shm_name, sizeof(shmarea->_name));
   shmarea->_name[sizeof(shmarea->_name) - 1] = 0;
+  shmarea->_local_addr = 0;
 
   if (l4_is_invalid_cap(shmarea->_shm_ds = l4re_util_cap_alloc()))
     return -L4_ENOMEM;
@@ -107,9 +115,15 @@ l4shmc_attach_to(const char *shm_name, l4_umword_t timeout_ms,
       goto out_free_cap;
     }
 
-  shmarea->_local_addr = 0;
-  if ((r = l4re_rm_attach(&shmarea->_local_addr,
-                          l4shmc_area_size(shmarea),
+  r = l4shmc_area_size(shmarea);
+  if (r < 0)
+    {
+      r = -L4_ENOMEM;
+      goto out_free_cap;
+    }
+  shmarea->_size = r;
+
+  if ((r = l4re_rm_attach(&shmarea->_local_addr, shmarea->_size,
                           L4RE_RM_SEARCH_ADDR, shmarea->_shm_ds,
                           0, L4_PAGESHIFT)))
     goto out_free_cap;
@@ -120,6 +134,58 @@ out_free_cap:
   return r;
 }
 
+L4_CV long
+l4shmc_area_overhead(void)
+{
+  return sizeof(shared_mem_t);
+}
+
+L4_CV long
+l4shmc_chunk_overhead(void)
+{
+  return sizeof(l4shmc_chunk_desc_t);
+}
+
+static long next_chunk(l4shmc_area_t *shmarea, l4_addr_t offs)
+{
+  shared_mem_t *shm_addr = (shared_mem_t *)shmarea->_local_addr;
+  volatile l4shmc_chunk_desc_t *p;
+  l4_addr_t next;
+
+  if (offs == 0)
+    {
+      next = shm_addr->_first_chunk;
+    }
+  else
+    {
+      p = chunk_get(offs, shmarea->_local_addr);
+      next = p->_next;
+    }
+  if (next == 0)
+    return 0;
+  if (next >= shmarea->_size || next + sizeof(*p) >= shmarea->_size || next <= offs)
+    return -L4_EIO;
+  if (next % sizeof(l4_addr_t) != 0)
+    return -L4_EINVAL;
+  p = chunk_get(next, shmarea->_local_addr);
+  if (p->_magic != L4SHMC_CHUNK_MAGIC)
+    return -L4_EIO;
+  return next;
+}
+
+L4_CV long
+l4shmc_iterate_chunk(l4shmc_area_t *shmarea, const char **chunk_name, long offs)
+{
+  if (offs < 0)
+    return -L4_EINVAL;
+  offs = next_chunk(shmarea, offs);
+  if (offs > 0)
+    {
+      l4shmc_chunk_desc_t *p = chunk_get(offs, shmarea->_local_addr);
+      *chunk_name =  p->_name;
+    }
+  return offs;
+}
 
 L4_CV long
 l4shmc_add_chunk(l4shmc_area_t *shmarea,
@@ -129,69 +195,91 @@ l4shmc_add_chunk(l4shmc_area_t *shmarea,
 {
   shared_mem_t *shm_addr = (shared_mem_t *)shmarea->_local_addr;
 
-  l4shmc_chunk_desc_t *p;
+  l4shmc_chunk_desc_t *p = NULL;
   l4shmc_chunk_desc_t *prev = NULL;
+  l4_addr_t offs = 0;
+  long ret;
 
-  shm_addr->lock = 0;
+  if (chunk_capacity >> (sizeof(chunk_capacity) * 8 - 1))
+    return -L4_ENOMEM;
 
   while (!l4util_cmpxchg(&shm_addr->lock, SHMAREA_LOCK_FREE,
                          SHMAREA_LOCK_TAKEN))
     l4_sleep(1);
   asm volatile ("" : : : "memory");
-  {
-    l4_addr_t offs;
-    long shm_sz;
-    if (shm_addr->_first_chunk)
-      {
-        offs = shm_addr->_first_chunk;
-        p = chunk_get(offs, shmarea->_local_addr);
-        do
-          {
-            offs = p->_offset + p->_capacity + sizeof(*p);
-            prev = p;
-            p = chunk_get(p->_next, shmarea->_local_addr);
-          }
-        while (prev->_next);
-      }
-    else
-      // first chunk starts right after shm-header
-      offs = sizeof(shared_mem_t);
-
-    if ((shm_sz = l4shmc_area_size(shmarea)) < 0)
-      goto out_free_lock;
-
-    if (offs + chunk_capacity + sizeof(*p) >= (unsigned long)shm_sz)
-      goto out_free_lock; // no more free memory in this shm
-
-    p = chunk_get(offs, shmarea->_local_addr);
-    p->_offset = offs;
-    p->_next = 0;
-    p->_capacity = chunk_capacity;
-    // Ensure that other CPUs have correct data before inserting chunk
-    __sync_synchronize();
-
-    if (prev)
-      prev->_next = offs;
-    else
-      shm_addr->_first_chunk = offs;
-  }
-  __sync_synchronize();
-  shm_addr->lock = SHMAREA_LOCK_FREE;
+  while ((ret = next_chunk(shmarea, offs)) > 0)
+    {
+      p = chunk_get(ret, shmarea->_local_addr);
+      if (strcmp(p->_name, chunk_name) == 0)
+        {
+          ret = -L4_EEXIST;
+          goto out_free_lock;
+        }
+      offs = ret;
+    }
+  if (ret < 0)
+     goto out_free_lock;
+  if (offs == 0)
+    offs = sizeof(shared_mem_t);
+  else
+    {
+      l4_addr_t n = p->_offset + p->_capacity + sizeof(*p);
+      if (n <= offs || n >= shmarea->_size)
+        {
+          ret = -L4_EIO;
+          goto out_free_lock;
+        }
+      offs = n;
+      prev = p;
+    }
 
+  if (offs + chunk_capacity + sizeof(*p) > (unsigned long)shmarea->_size)
+    {
+      ret = -L4_ENOMEM;
+      goto out_free_lock; // no more free memory in this shm
+    }
+  p = chunk_get(offs, shmarea->_local_addr);
+  p->_offset = offs;
+  p->_next = 0;
+  p->_capacity = chunk_capacity;
   p->_size = 0;
   p->_status = L4SHMC_CHUNK_CLEAR;
   p->_magic = L4SHMC_CHUNK_MAGIC;
   strncpy(p->_name, chunk_name, sizeof(p->_name));
   p->_name[sizeof(p->_name) - 1] = 0;
+  // Ensure that other CPUs have correct data before inserting chunk
+  l4shmc_membarrier();
+
+  if (prev)
+    prev->_next = offs;
+  else
+    shm_addr->_first_chunk = offs;
+
+  l4shmc_membarrier();
+  shm_addr->lock = SHMAREA_LOCK_FREE;
 
-  chunk->_chunk = p;
-  chunk->_shm    = shmarea;
-  chunk->_sig    = NULL;
+  chunk->_chunk    = p;
+  chunk->_shm      = shmarea;
+  chunk->_sig      = NULL;
+  chunk->_capacity = chunk_capacity;
 
   return L4_EOK;
 out_free_lock:
   shm_addr->lock = SHMAREA_LOCK_FREE;
-  return -L4_ENOMEM;
+  return ret;
+}
+
+L4_CV long
+l4shmc_area_size_free(l4shmc_area_t *shmarea)
+{
+  long ret;
+  l4_addr_t offs = 0;
+  while ((ret = next_chunk(shmarea, offs)) > 0)
+    offs = ret;
+  if (ret < 0)
+    return ret;
+  ret = shmarea->_size - offs;
+  return ret > 0 ? ret : 0;
 }
 
 L4_CV long
@@ -236,25 +324,30 @@ l4shmc_get_chunk_to(l4shmc_area_t *shmarea,
                     l4shmc_chunk_t *chunk)
 {
   l4_kernel_clock_t try_until = l4re_kip()->clock + (timeout_ms * 1000);
-  shared_mem_t *shm_addr = (shared_mem_t *)shmarea->_local_addr;
+  long ret;
 
   do
     {
-      l4_addr_t offs = shm_addr->_first_chunk;
-      while (offs)
+      l4_addr_t offs = 0;
+      while ((ret = next_chunk(shmarea, offs)) > 0)
         {
           l4shmc_chunk_desc_t *p;
+          offs = ret;
           p = chunk_get(offs, shmarea->_local_addr);
-
           if (!strcmp(p->_name, chunk_name))
             { // found it!
-               chunk->_shm    = shmarea;
-               chunk->_chunk = p;
-               chunk->_sig    = NULL;
+               chunk->_shm      = shmarea;
+               chunk->_chunk    = p;
+               chunk->_sig      = NULL;
+               chunk->_capacity = p->_capacity;
+               if (chunk->_capacity > shmarea->_size ||
+                   chunk->_capacity + offs > shmarea->_size)
+                  return -L4_EIO;
                return L4_EOK;
             }
-          offs = p->_next;
         }
+      if (ret < 0)
+        return ret;
 
       if (!timeout_ms)
         break;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.