ustream: prevent recursive calls to the read callback
[project/libubox.git] / udebug.c
index 00a66abd42e7b74c54e88f179dfee9101629b8e1..e39a32cb165f687325c7465d78b64c02a55e2713 100644 (file)
--- a/udebug.c
+++ b/udebug.c
@@ -37,6 +37,7 @@
 
 #define UDEBUG_MIN_ALLOC_LEN   128
 static struct blob_buf b;
+static unsigned int page_size;
 
 static void __randname(char *template)
 {
@@ -112,22 +113,30 @@ uint64_t udebug_timestamp(void)
 }
 
 static int
-__udebug_buf_map(struct udebug_buf *buf)
+__udebug_buf_map(struct udebug_buf *buf, int fd)
 {
+       unsigned int pad = 0;
        void *ptr, *ptr2;
 
-       ptr = mmap(NULL, buf->head_size + 2 * buf->data_size, PROT_NONE,
+#ifdef mips
+       pad = page_size;
+#endif
+       ptr = mmap(NULL, buf->head_size + 2 * buf->data_size + pad, PROT_NONE,
                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
        if (ptr == MAP_FAILED)
                return -1;
 
+#ifdef mips
+       ptr = (void *)ALIGN((unsigned long)ptr, page_size);
+#endif
+
        ptr2 = mmap(ptr, buf->head_size + buf->data_size,
-                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, buf->fd, 0);
+                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
        if (ptr2 != ptr)
                goto err_unmap;
 
        ptr2 = mmap(ptr + buf->head_size + buf->data_size, buf->data_size,
-                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, buf->fd,
+                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd,
                    buf->head_size);
        if (ptr2 != ptr + buf->head_size + buf->data_size)
                goto err_unmap;
@@ -290,8 +299,9 @@ recv_retry(int fd, struct iovec *iov, bool wait, int *recv_fd)
        return total;
 }
 
-void udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
-                    struct blob_attr *meta, int fd)
+static void
+udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
+               struct blob_attr *meta, int fd)
 {
        struct iovec iov[2] = {
                { .iov_base = msg, .iov_len = sizeof(*msg) },
@@ -308,6 +318,79 @@ void udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
        writev_retry(ctx->fd.fd, iov, ARRAY_SIZE(iov), fd);
 }
 
+static bool
+udebug_recv_msg(struct udebug *ctx, struct udebug_client_msg *msg, int *fd,
+               bool wait)
+{
+       struct iovec iov = {
+               .iov_base = msg,
+               .iov_len = sizeof(*msg)
+       };
+       int ret;
+
+       ret = recv_retry(ctx->fd.fd, &iov, wait, fd);
+       if (ret == -2)
+               __udebug_disconnect(ctx, true);
+
+       return ret == sizeof(*msg);
+}
+
+static struct udebug_client_msg *
+__udebug_poll(struct udebug *ctx, int *fd, bool wait)
+{
+       static struct udebug_client_msg msg = {};
+
+       while (udebug_recv_msg(ctx, &msg, fd, wait)) {
+               struct udebug_remote_buf *rb;
+               void *key;
+
+               if (msg.type != CL_MSG_RING_NOTIFY)
+                       return &msg;
+
+               if (fd && *fd >= 0)
+                       close(*fd);
+
+               if (!ctx->notify_cb)
+                       continue;
+
+               key = (void *)(uintptr_t)msg.id;
+               rb = avl_find_element(&ctx->remote_rings, key, rb, node);
+               if (!rb || !rb->poll)
+                       continue;
+
+               if (ctx->poll_handle >= 0)
+                       __atomic_fetch_or(&rb->buf.hdr->notify,
+                                         1UL << ctx->poll_handle,
+                                         __ATOMIC_RELAXED);
+               ctx->notify_cb(ctx, rb);
+       }
+
+       return NULL;
+}
+
+static struct udebug_client_msg *
+udebug_wait_for_response(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd)
+{
+       int type = msg->type;
+       int fd = -1;
+
+       do {
+               if (fd >= 0)
+                       close(fd);
+               fd = -1;
+               msg = __udebug_poll(ctx, &fd, true);
+       } while (msg && msg->type != type);
+       if (!msg)
+               return NULL;
+
+       if (rfd)
+               *rfd = fd;
+       else if (fd >= 0)
+               close(fd);
+
+       return msg;
+}
+
 static void
 udebug_buf_msg(struct udebug_buf *buf, enum udebug_client_msg_type type)
 {
@@ -317,26 +400,39 @@ udebug_buf_msg(struct udebug_buf *buf, enum udebug_client_msg_type type)
        };
 
        udebug_send_msg(buf->ctx, &msg, NULL, -1);
+       udebug_wait_for_response(buf->ctx, &msg, NULL);
 }
 
-static size_t __udebug_headsize(unsigned int ring_size, unsigned int page_size)
+static size_t __udebug_headsize(unsigned int ring_size)
 {
        ring_size *= sizeof(struct udebug_ptr);
        return ALIGN(sizeof(struct udebug_hdr) + ring_size, page_size);
 }
 
+static void udebug_init_page_size(void)
+{
+       if (page_size)
+               return;
+       page_size = sysconf(_SC_PAGESIZE);
+#ifdef mips
+       /* leave extra alignment room to account for data cache aliases */
+       if (page_size < 32 * 1024)
+               page_size = 32 * 1024;
+#endif
+}
+
 int udebug_buf_open(struct udebug_buf *buf, int fd, uint32_t ring_size, uint32_t data_size)
 {
+       udebug_init_page_size();
        INIT_LIST_HEAD(&buf->list);
-       buf->fd = fd;
        buf->ring_size = ring_size;
-       buf->head_size = __udebug_headsize(ring_size, sysconf(_SC_PAGESIZE));
+       buf->head_size = __udebug_headsize(ring_size);
        buf->data_size = data_size;
 
        if (buf->ring_size > (1U << 24) || buf->data_size > (1U << 29))
                return -1;
 
-       if (__udebug_buf_map(buf))
+       if (__udebug_buf_map(buf, fd))
                return -1;
 
        if (buf->ring_size != buf->hdr->ring_size ||
@@ -346,21 +442,23 @@ int udebug_buf_open(struct udebug_buf *buf, int fd, uint32_t ring_size, uint32_t
                return -1;
        }
 
+       buf->fd = fd;
+
        return 0;
 }
 
 int udebug_buf_init(struct udebug_buf *buf, size_t entries, size_t size)
 {
-       uint32_t pagesz = sysconf(_SC_PAGESIZE);
        char filename[] = "/udebug.XXXXXX";
        unsigned int order = 12;
        uint8_t ring_order = 5;
        size_t head_size;
        int fd;
 
+       udebug_init_page_size();
        INIT_LIST_HEAD(&buf->list);
-       if (size < pagesz)
-               size = pagesz;
+       if (size < page_size)
+               size = page_size;
        while(size > 1U << order)
                order++;
        size = 1 << order;
@@ -371,8 +469,8 @@ int udebug_buf_init(struct udebug_buf *buf, size_t entries, size_t size)
        if (size > (1U << 29) || entries > (1U << 24))
                return -1;
 
-       head_size = __udebug_headsize(entries, pagesz);
-       while (ALIGN(sizeof(*buf->hdr) + (entries * 2) * sizeof(struct udebug_ptr), pagesz) == head_size)
+       head_size = __udebug_headsize(entries);
+       while (ALIGN(sizeof(*buf->hdr) + (entries * 2) * sizeof(struct udebug_ptr), page_size) == head_size)
                entries *= 2;
 
        fd = shm_open_anon(filename);
@@ -385,11 +483,11 @@ int udebug_buf_init(struct udebug_buf *buf, size_t entries, size_t size)
        buf->head_size = head_size;
        buf->data_size = size;
        buf->ring_size = entries;
-       buf->fd = fd;
 
-       if (__udebug_buf_map(buf))
+       if (__udebug_buf_map(buf, fd))
                goto err_close;
 
+       buf->fd = fd;
        buf->hdr->ring_size = entries;
        buf->hdr->data_size = size;
 
@@ -470,8 +568,12 @@ void *udebug_entry_append(struct udebug_buf *buf, const void *data, uint32_t len
 uint16_t udebug_entry_trim(struct udebug_buf *buf, uint16_t len)
 {
        struct udebug_hdr *hdr = buf->hdr;
-       struct udebug_ptr *ptr = udebug_ring_ptr(hdr, hdr->head);
+       struct udebug_ptr *ptr;
 
+       if (!hdr)
+               return 0;
+
+       ptr = udebug_ring_ptr(hdr, hdr->head);
        if (len)
                ptr->len -= len;
 
@@ -481,8 +583,12 @@ uint16_t udebug_entry_trim(struct udebug_buf *buf, uint16_t len)
 void udebug_entry_set_length(struct udebug_buf *buf, uint16_t len)
 {
        struct udebug_hdr *hdr = buf->hdr;
-       struct udebug_ptr *ptr = udebug_ring_ptr(hdr, hdr->head);
+       struct udebug_ptr *ptr;
 
+       if (!hdr)
+               return;
+
+       ptr = udebug_ring_ptr(hdr, hdr->head);
        ptr->len = len;
 }
 
@@ -504,6 +610,7 @@ int udebug_entry_vprintf(struct udebug_buf *buf, const char *fmt, va_list ap)
        struct udebug_ptr *ptr;
        uint32_t ofs;
        uint32_t len;
+       va_list ap2;
        char *str;
 
        if (!hdr)
@@ -515,7 +622,9 @@ int udebug_entry_vprintf(struct udebug_buf *buf, const char *fmt, va_list ap)
                return -1;
 
        str = udebug_buf_alloc(buf, ofs, UDEBUG_MIN_ALLOC_LEN);
-       len = vsnprintf(str, UDEBUG_MIN_ALLOC_LEN, fmt, ap);
+       va_copy(ap2, ap);
+       len = vsnprintf(str, UDEBUG_MIN_ALLOC_LEN, fmt, ap2);
+       va_end(ap2);
        if (len <= UDEBUG_MIN_ALLOC_LEN)
                goto out;
 
@@ -533,10 +642,15 @@ out:
 void udebug_entry_add(struct udebug_buf *buf)
 {
        struct udebug_hdr *hdr = buf->hdr;
-       struct udebug_ptr *ptr = udebug_ring_ptr(hdr, hdr->head);
+       struct udebug_ptr *ptr;
        uint32_t notify;
        uint8_t *data;
 
+       if (!hdr)
+               return;
+
+       ptr = udebug_ring_ptr(hdr, hdr->head);
+
        /* ensure strings are always 0-terminated */
        data = udebug_buf_ptr(buf, ptr->start + ptr->len);
        *data = 0;
@@ -604,11 +718,15 @@ __udebug_buf_add(struct udebug *ctx, struct udebug_buf *buf)
        blobmsg_close_array(&b, c);
 
        udebug_send_msg(ctx, &msg, b.head, buf->fd);
+       udebug_wait_for_response(ctx, &msg, NULL);
 }
 
 int udebug_buf_add(struct udebug *ctx, struct udebug_buf *buf,
                   const struct udebug_buf_meta *meta)
 {
+       if (!buf->hdr)
+               return -1;
+
        list_add_tail(&buf->list, &ctx->local_rings);
        buf->ctx = ctx;
        buf->meta = meta;
@@ -683,58 +801,17 @@ int udebug_connect(struct udebug *ctx, const char *path)
        return 0;
 }
 
-static bool
-udebug_recv_msg(struct udebug *ctx, struct udebug_client_msg *msg, int *fd,
-               bool wait)
+void udebug_poll(struct udebug *ctx)
 {
-       struct iovec iov = {
-               .iov_base = msg,
-               .iov_len = sizeof(*msg)
-       };
-       int ret;
-
-       ret = recv_retry(ctx->fd.fd, &iov, wait, fd);
-       if (ret == -2)
-               __udebug_disconnect(ctx, true);
-
-       return ret == sizeof(*msg);
+       while (__udebug_poll(ctx, NULL, false));
 }
 
-struct udebug_client_msg *__udebug_poll(struct udebug *ctx, int *fd, bool wait)
+struct udebug_client_msg *
+udebug_send_and_wait(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd)
 {
-       static struct udebug_client_msg msg = {};
-
-       while (udebug_recv_msg(ctx, &msg, fd, wait)) {
-               struct udebug_remote_buf *rb;
-               void *key;
-
-               if (msg.type != CL_MSG_RING_NOTIFY)
-                       return &msg;
-
-               if (fd && *fd >= 0)
-                       close(*fd);
-
-               if (!ctx->notify_cb)
-                       continue;
-
-               key = (void *)(uintptr_t)msg.id;
-               rb = avl_find_element(&ctx->remote_rings, key, rb, node);
-               if (!rb || !rb->poll)
-                       continue;
+       udebug_send_msg(ctx, msg, NULL, -1);
 
-               if (ctx->poll_handle >= 0)
-                       __atomic_fetch_or(&rb->buf.hdr->notify,
-                                         1UL << ctx->poll_handle,
-                                         __ATOMIC_RELAXED);
-               ctx->notify_cb(ctx, rb);
-       }
-
-       return NULL;
-}
-
-void udebug_poll(struct udebug *ctx)
-{
-       while (__udebug_poll(ctx, NULL, false));
+       return udebug_wait_for_response(ctx, msg, rfd);
 }
 
 static void udebug_fd_cb(struct uloop_fd *fd, unsigned int events)