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;