linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* NUMA allocations fail to be numa allocated
@ 2013-02-12 21:27 Professor Berkley Shands
  0 siblings, 0 replies; only message in thread
From: Professor Berkley Shands @ 2013-02-12 21:27 UTC (permalink / raw)
  To: linux-kernel

using libnuma calls on RedHat 6.3 x86_64 with the default kernel and up 
to 3.4.29
don't allocate on the specified numa nodes, even when forced with numactl.

It appears that setting the NUMA policy, and or numa nodes does little 
for large allocations.
Using HUGETLBFS, and you get memory on most any node BUT the one you 
asked for.
It appears that it allocates on the last node that did a free().

Here is a small program to demo the lack of numa awareness from user space.

#include <stdlib.h>
#include <sys/shm.h>
#include <stdio.h>
#include <numa.h>
#include <numaif.h>
#include <sched.h>              // for sched_getcpu() call

static const unsigned long HUGE_PAGE_SIZE = 1UL << 21;  // a 2MB huge page
static const unsigned long HUGE_PAGE_SIZE1 = (1UL << 21) - 1;   // less one
static const unsigned long PAGE_SIZE = 1UL << 10;       // a 4KB page
static const unsigned long PAGE_SIZE1 = (1UL << 10) - 1;        // less one

int VerifyNumaNode(void *ptr,   // address
                    int node,    // target node
                    int Count);  // count of 4KB pages
int MoveAddrToNodeMulti(void *ptr, int node, int Count);

void *Allocate(size_t length, int OnNode)
{
    int shmid = -1;
    void *shmaddr = NULL;
    size_t new_length = length;
    int MaxNumaNode = numa_max_node();   // find highest NUMA number
    int LocalNumaNode = numa_node_of_cpu(sched_getcpu());
    int NewNumaNode = LocalNumaNode;
    unsigned long MaskBits[2] = { 0UL, 0UL };    // up to 128 nodes
    struct bitmask NewMask;
    NewMask.size = 8;            // Max nodes on an HP
    struct bitmask *CurrentMask = numa_get_membind();

    // see if NUMA allocation is desired
    if (OnNode >= 0)
    {
       if (OnNode > MaxNumaNode)
       {
          fprintf(stderr, "Invalid NUMA HUGEPages allocation node %d max 
is %d\n", OnNode, MaxNumaNode);
       }
       else
       {
          NewNumaNode = OnNode;
       }
    }
    MaskBits[0] = 1UL << NewNumaNode;
    numa_set_membind(&NewMask);  // restrict to this node

    if (new_length < HUGE_PAGE_SIZE)     /* 2MB min alloc for huge pages */
    {
       new_length = HUGE_PAGE_SIZE;
    }

    if (new_length & HUGE_PAGE_SIZE1)    /* 2MB min alloc for huge pages */
    {
       new_length = ((new_length >> 21) + 1) << 21;
    }
    if ((shmid = shmget(IPC_PRIVATE, new_length, /* length */
                        SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) == -1)
    {
       fprintf(stderr, "shmget() failed for %ldMB\n", (long) (new_length 
 >> 20));
       numa_set_membind(CurrentMask);    // unrestrict to this node
       return NULL;
    }

    shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *) -1)
    {
       shmctl(shmid, IPC_RMID, NULL);
       numa_set_membind(CurrentMask);    // unrestrict to this node
       return NULL;
    }
    else if ((unsigned long) (shmaddr) & (PAGE_SIZE - 1))
    {
       fprintf(stderr, "huge page allocation was not page aligned\n");
    }

    memset(shmaddr, 0x00, new_length);
    if (VerifyNumaNode(shmaddr, NewNumaNode, new_length / 4096UL) > 0)
    {
       MoveAddrToNodeMulti(shmaddr, NewNumaNode, new_length / 4096UL);
    }
    numa_set_membind(CurrentMask);       // unrestrict to this node
    VerifyNumaNode(shmaddr, NewNumaNode, new_length / 4096UL);
    /* now delete the ID so it will free itself on exit */
    shmctl(shmid, IPC_RMID, NULL);
    return shmaddr;
}

void Free(void *addr)
{
}

int NumaNodeFromAddress(void *Address)
{
    int status[1] = { -1 };
    void *PTR = Address;
    void *PTR2[1] = { NULL };
    PTR2[0] = &PTR;
    int retval = move_pages(0,   // this thread
                            1,   // just one pointer
                            PTR2,        // The given address
                            NULL,        // array of nodes, no moving, 
just asking
                            status,      // array of node results
                            MPOL_MF_MOVE);
    if (retval)
    {
       fprintf(stderr, "Invalid Address %p - No NUMA node\n", Address);
    }
    return status[0];
}

int MoveAddrToNodeMulti(void *ptr, int node, int Count)
{
    unsigned long *PTR = new unsigned long[Count + 1];
    unsigned long *PTR2 = new unsigned long[Count + 1];
    int *status = new int[Count + 1];
    int *NN = new int[Count + 1];
    int retval = 0;
    int i = 0;
    unsigned long addr = 0;

    for (i = 0; i < Count; i++)
    {
       status[i] = -1;
       NN[i] = node;
       addr = ((unsigned long) ptr) + (i * 4096);
       PTR[i] = (unsigned long) (addr & ~4095UL);
       PTR2[i] = (unsigned long) &PTR[i];
    }

    retval = move_pages(0,       // this thread
                        Count,   // lots of pointers
                        (void **) PTR2,  // The given address
                        NN,      // move to new node please
                        status,  // array of node results
                        MPOL_MF_MOVE);
    if (retval)
    {
       fprintf(stderr, "MoveAddrToNodeMulti to failed\n");
    }
    else
    {
       retval = 0;
       for (i = 0; i < Count; i++)
       {
          if (status[i] != node)
          {
             fprintf(stderr, "Addr 0x%08lx is node %d not %d\n", PTR[i], 
status[i], node);
             retval++;
          }
       }
    }
    delete [] NN;
    delete [] status;
    delete [] PTR2;
    delete [] PTR;
    return retval;
}

int VerifyNumaNode(void *ptr, int node, int Count)
{
    unsigned long *PTR = new unsigned long[Count + 1];
    unsigned long *PTR2 = new unsigned long[Count + 1];
    int *status = new int[Count + 1];
    int retval = 0;
    int i = 0;
    unsigned long addr = 0;

    for (i = 0; i < Count; i++)
    {
       status[i] = -1;
       addr = ((unsigned long) ptr) + (i * 4096);
       PTR[i] = (unsigned long) (addr & ~4095UL);
       PTR2[i] = (unsigned long) &PTR[i];
    }

    retval = move_pages(0,       // this thread
                        Count,   // lots of pointers
                        (void **) PTR2,  // The given address
                        NULL,    // no new node
                        status,  // array of node results
                        MPOL_MF_MOVE);
    if (retval)
    {
       fprintf(stderr, "VerifyNumaNode move_pages failed\n");
    }
    else
    {
       retval = 0;
       for (i = 0; i < Count; i++)
       {
          if (status[i] != node)
          {
             fprintf(stderr, "Verify Addr 0x%08lx is node %d not %d\n", 
PTR[i], status[i], node);
             retval++;
          }
       }
    }

    // release temp stuff

    delete [] status;
    delete [] PTR2;
    delete [] PTR;
    return retval;
}

// small demo program showing:
//
// a: huge page allocations via hugetlb are not node allocated
// b: huge pages cannot be move_page()'ed
// c: Replacing the shm*() with numa_alloc_node() has the exact same problem
// d: 4KB pages or 2MB pages act the same.

int main(int argc, char **argv)
{
    int Node = -1;
    unsigned long Size = 32UL * 1024UL *1024UL;  // default to 32MB

    if (argc >= 2)
    {
       Node = atoi(argv[1]);
    }
    if (argc >= 3)
    {
       Size = atol(argv[2]) * 1024UL * 1024UL;
    }

    unsigned long *Array = (unsigned long *) Allocate(Size, Node);
    exit(-1);
}



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2013-02-12 21:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-12 21:27 NUMA allocations fail to be numa allocated Professor Berkley Shands

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).