[lkml]   [2000]   [Jul]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
Messages in this thread
    Subject[RFC] solution for the inet_ntoa problem, buffer allocator
    Ideally, we would be able to do something like:
    printk(KERN_DEBUG "foo: src=%s dst=%s\n", inet_ntoa(src), inet_ntoa(dst));
    but the inet_ntoa() API doesn't work that way. Providing a buffer to
    that function needs additional code over and over again, and that is
    exactly what such library functions should eliminate.
    The NIPQUAD solution is (a) cumbersome to handle, (b) conceptually
    wrong since an IP address is _one_ item to print, not four, and (c)
    even worse when dealing with IPv6 addresses.

    Some kind of dynamic allocation of buffers would be in order.
    Unfortunately, alloca() can not be used in this context.

    I'm presenting a simple routine to dynamically allocate buffers and
    implementations of
    char *kinet_ntoa(__u32 a) and
    char *kinet6_ntoa(const struct in6_addr *a)
    which use this allocation. With those routines, it is possible to call
    printk() in the natural way. For the IPv6 case, the address is
    displayed in compressed format. It is easy to add any other
    "foo-to-string" conversion routine.

    The buffer allocation works as follows: every conversion routine has
    its own chain (salloc_t) of buffers (scope_buf_t). Whenever a buffer
    is needed, it looks for a free one in the chain. Each buffer is tagged
    with the return address of the caller (here called "scope"). A buffer
    is considered free when its scope doesn't match the current one.
    Additionally, since one buffer can be used several times in a row by
    one caller, a buffer is considered free if it is older than an hour.
    When no free buffer is found, a new one is allocated. A routine to
    free the whole chain is provided too.
    Each chain is protected by its own spinlock and the whole thing is
    (assumed to be) SMP-safe.

    I assume that using __builtin_frame_address(0) as scope instead of
    __builtin_return_address(0) would lead to less collisions, but this is
    not tested and using __builtin_frame_address in a function seems to
    automatically turn on -fno-omit-frame-pointer for that function, so
    this is prone to hurt more than it helps.

    The central salloc() routine is non-reentrant wrt. one chain and the
    buffers it returns are assumed to be short-lived. It is constructed
    precisely for the printk("%s", foo(bar)) or strcpy(dest, foo(bar))
    situation where foo() calls salloc() to get its buffer and probably
    unsuited for other uses.

    The buffer chains are like micro-slabs but using a kmem_cache for each
    one of them, which is assumed to hold only 3-4 objects of size 16
    bytes (for kinet_ntoa), would probably be overkill.

    I currently use this module in CIPE and it works. (The IPv6 part is
    more a demo by now, since CIPE doesn't use IPv6, but it is tested.)


    #ifndef SALLOC_H
    #define SALLOC_H

    #define SALLOC_DEBUG
    #define SALLOC_TIMEOUT (3600*HZ)

    #include <linux/types.h>
    #include <linux/socket.h>
    #include <linux/in.h>
    #include <linux/in6.h>
    #include <linux/malloc.h>
    #include <asm/spinlock.h>

    typedef struct scope_buf_s {
    struct scope_buf_s *next;
    void *scope;
    unsigned long timeout;
    char buf[0];
    } scope_buf_t;

    typedef struct salloc_s {
    scope_buf_t *buflist;
    int size;
    spinlock_t lock;
    #ifdef SALLOC_DEBUG
    int nbufs;
    } salloc_t;

    #ifdef SALLOC_DEBUG
    #define SALLOC_INIT(size) (salloc_t) { NULL, size, SPIN_LOCK_UNLOCKED, 0 }
    #define SALLOC_INIT(size) (salloc_t) { NULL, size, SPIN_LOCK_UNLOCKED }

    extern void *salloc(salloc_t *bufs, void *scope);

    extern void sunalloc(salloc_t *bufs);

    /* These routines use salloc() and can be seen as a prototype. */

    extern char *__kinet_ntoa(__u32 a, void *s);
    #define kinet_ntoa(a) __kinet_ntoa((a), __builtin_return_address(0))

    extern char *__kinet6_ntoa(const struct in6_addr *a, void *s);
    #define kinet6_ntoa(a) __kinet6_ntoa((a), __builtin_return_address(0))

    extern void ntoa_unalloc(void);

    #include "salloc.h"

    #include <linux/kernel.h>

    #define IN6_COMPRESS

    void *salloc(salloc_t *bufs, void *scope)
    unsigned long flags;
    scope_buf_t *q;
    spin_lock_irqsave(&bufs->lock, flags);
    q = bufs->buflist;

    while (q) {
    if (q->scope != scope)
    goto out;
    if (time_after(jiffies, q->timeout)) {
    printk(KERN_INFO "salloc: timeout buf=%p scope=%p\n",
    q, q->scope);
    /* Break this because it _should_ be dead */
    goto out;
    q = q->next;
    #ifdef SALLOC_DEBUG
    printk(KERN_DEBUG "salloc: alloc %d n=%d scope=%p\n",
    bufs->size, bufs->nbufs, scope);

    q = kmalloc(sizeof(scope_buf_t)+bufs->size, GFP_ATOMIC);
    q->next = bufs->buflist;
    bufs->buflist = q;

    q->scope = scope;
    q->timeout = jiffies + SALLOC_TIMEOUT;

    spin_unlock_irqrestore(&bufs->lock, flags);
    return q->buf;

    void sunalloc(salloc_t *bufs)
    unsigned long flags;
    scope_buf_t *q, *q0;
    spin_lock_irqsave(&bufs->lock, flags);
    for (q = bufs->buflist; q; q = q0) {
    q0 = q->next;
    bufs->buflist = NULL;
    #ifdef SALLOC_DEBUG
    bufs->nbufs = 0;
    spin_unlock_irqrestore(&bufs->lock, flags);

    /* IPv4 address conversion */

    static salloc_t sa_ntoa = SALLOC_INIT(16);

    static inline void ___kinet_ntoa(const __u32 a, char *b)
    char *p = b;
    const unsigned char *x=(const unsigned char *)&a;
    int i;
    for (i=0; i<4; ++i, ++x) {
    int k=*x/100;
    if (k)
    if (k)
    if (i<3)

    char *__kinet_ntoa(const __u32 a, void *s)
    char *b = salloc(&sa_ntoa, s);
    ___kinet_ntoa(a, b);
    return b;

    /* IPv6 address conversion */

    static salloc_t sa_ntoa6 = SALLOC_INIT(40);

    #ifdef IN6_COMPRESS
    /* Find longest zero run of 16bit words */
    static inline void flzr(const struct in6_addr *a, int *l, int *r)
    int i=0, j;
    while (i<8) {
    if (!a->s6_addr16[i]) {
    j = i;
    while (j<7 && !a->s6_addr16[j+1])
    if (j-i > *r-*l) {
    *l = i;
    *r = j;
    i = j+1;
    } else {

    static inline void ___kinet6_ntoa(const struct in6_addr *a, char *b)
    char *p = b;
    int i, j;
    int l = -1, r = -2;
    static const char hex[16] = "0123456789ABCDEF";
    static const __u16 mask[4] = { 0xF000, 0xFF00, 0xFFF0, 0xFFFF };
    static const __u16 shft[4] = { 12, 8, 4, 0 };
    #ifdef IN6_COMPRESS
    flzr(a, &l, &r);
    if (l==0)
    *p++ = ':';
    for (i=0; i<8; ++i) {
    if (i<l || i>r) {
    __u16 k=ntohs(a->s6_addr16[i]);
    for (j=0; j<4; ++j)
    if (j==3 || (k & mask[j]))
    *p++ = hex[(k>>shft[j]) & 15];
    if (i<7)
    *p++ = ':';
    if (i==r)
    *p++ = ':';
    *p = '\0';

    char *__kinet6_ntoa(const struct in6_addr *a, void *s)
    char *b = salloc(&sa_ntoa6, s);
    ___kinet6_ntoa(a, b);
    return b;

    void ntoa_unalloc(void)
     \ /
      Last update: 2005-03-22 13:57    [W:0.051 / U:40.916 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site