/*
 * nasd_linux_mem.h
 *
 * linux-specific memory allocation
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#ifndef _NASD__NASD_LINUX_MEM_H_
#define _NASD__NASD_LINUX_MEM_H_

#include <nasd/nasd_threadstuff.h>

#ifdef KERNEL

/*
 * The "5" is theoretically NR_MEM_LISTS-1. NR_MEM_LISTS
 * is N+1 where 2^N is the maximum number of pages we
 * can allocate. It is not exported anywhere. Yippee.
 */

#define NASD_LINUX_MAXORD        5
#define NASD_LINUX_MAX_MALLOC    ((1<<NASD_LINUX_MAXORD)*PAGE_SIZE)
#define NASD_LINUX_ONEPAGE       PAGE_SIZE
#define NASD_LINUX_TWOPAGE       (2*PAGE_SIZE)
#define NASD_LINUX_FOURPAGE      (4*PAGE_SIZE)
#define NASD_LINUX_EIGHTPAGE     (8*PAGE_SIZE)
#define NASD_LINUX_SIXTEENPAGE   (16*PAGE_SIZE)
#define NASD_LINUX_THIRTYTWOPAGE (32*PAGE_SIZE)

#define NASD_SYS_MALLOC_PGPWER_DMA(_p_,_cast_,_pwr_) { \
  NASD_ASSERT((_pwr_) <= NASD_LINUX_MAXORD); \
  _p_ = _cast_ __get_free_pages(GFP_KERNEL|GFP_DMA, _pwr_); \
  if ((_p_) == NULL) { \
    nasd_printf("Failed allocating DMA pages pwr %d at %s:%d\n",\
       (int)_pwr_, __FILE__, __LINE__); \
  } \
}

#define NASD_SYS_MALLOC_PGPWER(_p_,_cast_,_pwr_) { \
  NASD_ASSERT((_pwr_) <= NASD_LINUX_MAXORD); \
  _p_ = _cast_ __get_free_pages(GFP_KERNEL, _pwr_); \
  if ((_p_) == NULL) { \
    nasd_printf("(1) Failed allocating pages pwr %d at %s:%d\n",\
       (int)_pwr_, __FILE__, __LINE__); \
    nasd_sys_thread_yield(); \
    _p_ = _cast_ __get_free_pages(GFP_KERNEL, _pwr_); \
    if ((_p_) == NULL) { \
      nasd_printf("(2) Failed allocating pages pwr %d at %s:%d\n",\
         (int)_pwr_, __FILE__, __LINE__); \
      /* I _almost_ feel bad about this. Almost. */ \
      nasd_sys_thread_yield(); \
      _p_ = _cast_ __get_free_pages(__GFP_HIGH|__GFP_WAIT|__GFP_IO, _pwr_); \
      /* Be nice. Yield again. */ \
      nasd_sys_thread_yield(); \
      if ((_p_) == NULL) { \
        nasd_printf("(3) Failed allocating pages pwr %d at %s:%d\n",\
           (int)_pwr_, __FILE__, __LINE__); \
      } \
    } \
  } \
}

#define NASD_SYS_FREE_PGPWER(_p_,_pwr_) { \
  NASD_ASSERT((_pwr_) <= NASD_LINUX_MAXORD); \
  free_pages((unsigned long)_p_, _pwr_); \
}

#define NASD_SYS_MALLOC_MIGHTPAGE(_p_,_cast_,_size_) { \
  if ((_size_) == NASD_LINUX_ONEPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,0); \
  } \
  else if ((_size_) == NASD_LINUX_TWOPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,1); \
  } \
  else if ((_size_) == NASD_LINUX_FOURPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,2); \
  } \
  else if ((_size_) == NASD_LINUX_EIGHTPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,3); \
  } \
  else if ((_size_) == NASD_LINUX_SIXTEENPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,4); \
  } \
  else if ((_size_) == NASD_LINUX_THIRTYTWOPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,5); \
  } \
  else { \
    _p_ = _cast_ vmalloc(_size_); \
    if ((_p_) == NULL) { \
      nasd_printf("Rejecting vmalloc size %ld %s:%d\n", (long)_size_, __FILE__, __LINE__); \
      nasd_sys_thread_yield(); \
      _p_ = _cast_ vmalloc(_size_); \
      if ((_p_) == NULL) { \
        nasd_printf("Rejecting vmalloc size %ld %s:%d try 2\n", (long)_size_, __FILE__, __LINE__); \
        nasd_sys_thread_yield(); \
        _p_ = _cast_ vmalloc(_size_); \
        if ((_p_) == NULL) { \
          nasd_printf("Rejecting vmalloc size %ld %s:%d try 3\n", (long)_size_, __FILE__, __LINE__); \
        } \
      } \
    } \
  } \
}

#define NASD_GFP_SRPC (__GFP_HIGH|__GFP_WAIT|__GFP_IO)

#define NASD_SYS_MALLOC_WIRED(_p_,_cast_,_size_) { \
  if ((_size_) == NASD_LINUX_ONEPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,0); \
  } \
  else if ((_size_) == NASD_LINUX_TWOPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,1); \
  } \
  else if ((_size_) == NASD_LINUX_FOURPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,2); \
  } \
  else if ((_size_) == NASD_LINUX_EIGHTPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,3); \
  } \
  else if ((_size_) == NASD_LINUX_SIXTEENPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,4); \
  } \
  else if ((_size_) == NASD_LINUX_THIRTYTWOPAGE) { \
    NASD_SYS_MALLOC_PGPWER(_p_,_cast_,5); \
  } \
  else { \
    _p_ = _cast_ kmalloc(_size_, NASD_GFP_SRPC); \
    if ((_p_) == NULL) { \
      nasd_printf("Rejecting kmalloc size %ld %s:%d\n", (long)_size_, __FILE__, __LINE__); \
      nasd_sys_thread_yield(); \
      _p_ = _cast_ kmalloc(_size_, NASD_GFP_SRPC); \
      if ((_p_) == NULL) { \
        nasd_printf("Rejecting kmalloc size %ld %s:%d try 2\n", (long)_size_, __FILE__, __LINE__); \
        nasd_sys_thread_yield(); \
        _p_ = _cast_ kmalloc(_size_, NASD_GFP_SRPC); \
        if ((_p_) == NULL) { \
          nasd_printf("Rejecting kmalloc size %ld %s:%d try 3\n", (long)_size_, __FILE__, __LINE__); \
        } \
      } \
    } \
  } \
}

#define NASD_SYS_FREE_MIGHTPAGE(_p_,_size_) { \
  if ((_size_) == NASD_LINUX_ONEPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,0); \
  } \
  else if ((_size_) == NASD_LINUX_TWOPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,1); \
  } \
  else if ((_size_) == NASD_LINUX_FOURPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,2); \
  } \
  else if ((_size_) == NASD_LINUX_EIGHTPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,3); \
  } \
  else if ((_size_) == NASD_LINUX_SIXTEENPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,4); \
  } \
  else if ((_size_) == NASD_LINUX_THIRTYTWOPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,5); \
  } \
  else { \
    vfree(_p_); \
  } \
}

#define NASD_SYS_FREE_WIRED(_p_,_size_) { \
  if ((_size_) == NASD_LINUX_ONEPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,0); \
  } \
  else if ((_size_) == NASD_LINUX_TWOPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,1); \
  } \
  else if ((_size_) == NASD_LINUX_FOURPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,2); \
  } \
  else if ((_size_) == NASD_LINUX_EIGHTPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,3); \
  } \
  else if ((_size_) == NASD_LINUX_SIXTEENPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,4); \
  } \
  else if ((_size_) == NASD_LINUX_THIRTYTWOPAGE) { \
    NASD_SYS_FREE_PGPWER(_p_,5); \
  } \
  else { \
    kfree(_p_); \
  } \
}

#if NASD_LINUX_FORCE_NOPAGE > 0

#define NASD_SYS_MALLOC(_p_,_cast_,_size_) \
  NASD_SYS_MALLOC_WIRED(_p_,_cast_,_size_)

/* XXX we can certainly get page-aligned memory in the kernel.
   we just don't do it yet because nothing needs it. */
#define NASD_SYS_VALLOC(_p_,_cast_,_size_) \
  NASD_SYS_MALLOC(_p_,_cast_,_size_)

#define NASD_SYS_FREE(_p_,_size_) \
  NASD_SYS_FREE_WIRED(_p_,_size_)

#else /* NASD_LINUX_FORCE_NOPAGE > 0 */

#define NASD_SYS_MALLOC(_p_,_cast_,_size_) \
  NASD_SYS_MALLOC_MIGHTPAGE(_p_,_cast_,_size_)

/* XXX we can certainly get page-aligned memory in the kernel.
   we just don't do it yet because nothing needs it. */
#define NASD_SYS_VALLOC(_p_,_cast_,_size_) \
  NASD_SYS_MALLOC(_p_,_cast_,_size_)

#define NASD_SYS_FREE(_p_,_size_) \
  NASD_SYS_FREE_MIGHTPAGE(_p_,_size_)

#endif /* NASD_LINUX_FORCE_NOPAGE > 0 */

#else /* !KERNEL */

#if NASD_USE_GLIBC_MALLOC_TRACING > 0

extern void *_nasd_linux_malloc(int,char*,int);
extern void _nasd_linux_free(void*,char*,int);

#define NASD_MEM_SYS_INIT_CODE {                                          \
  extern void mtrace(void);                                               \
  mtrace();                                                               \
}

#define NASD_SYS_MALLOC(_p_,_cast_,_sz_)                                  \
                 _p_ = _cast_ _nasd_linux_malloc(_sz_,__FILE__,__LINE__)
#define NASD_SYS_FREE(_p_,_sz_)                                           \
                 _nasd_linux_free(_p_,__FILE__,__LINE__)

#endif /* NASD_USE_GLIBC_MALLOC_TRACING > 0 */

#endif /* KERNEL */

#endif /* !_NASD__NASD_LINUX_MEM_H_ */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
