/* * include/haproxy/shctx.h - shared context management functions for SSL * * Copyright (C) 2011-2012 EXCELIANCE * * Author: Emeric Brun - emeric@exceliance.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef __HAPROXY_SHCTX_H #define __HAPROXY_SHCTX_H #include #include #include #ifndef USE_PRIVATE_CACHE #ifdef USE_PTHREAD_PSHARED #include #else #ifdef USE_SYSCALL_FUTEX #include #include #include #endif #endif #endif int shctx_init(struct shared_context **orig_shctx, int maxblocks, int blocksize, unsigned int maxobjsz, int extra, int shared); struct shared_block *shctx_row_reserve_hot(struct shared_context *shctx, struct shared_block *last, int data_len); void shctx_row_inc_hot(struct shared_context *shctx, struct shared_block *first); void shctx_row_dec_hot(struct shared_context *shctx, struct shared_block *first); int shctx_row_data_append(struct shared_context *shctx, struct shared_block *first, struct shared_block *from, unsigned char *data, int len); int shctx_row_data_get(struct shared_context *shctx, struct shared_block *first, unsigned char *dst, int offset, int len); /* Lock functions */ #if defined (USE_PRIVATE_CACHE) #define shctx_lock(shctx) #define shctx_unlock(shctx) #elif defined (USE_PTHREAD_PSHARED) extern int use_shared_mem; #define shctx_lock(shctx) if (use_shared_mem) pthread_mutex_lock(&shctx->mutex) #define shctx_unlock(shctx) if (use_shared_mem) pthread_mutex_unlock(&shctx->mutex) #else extern int use_shared_mem; #ifdef USE_SYSCALL_FUTEX static inline void _shctx_wait4lock(unsigned int *count, unsigned int *uaddr, int value) { syscall(SYS_futex, uaddr, FUTEX_WAIT, value, NULL, 0, 0); } static inline void _shctx_awakelocker(unsigned int *uaddr) { syscall(SYS_futex, uaddr, FUTEX_WAKE, 1, NULL, 0, 0); } #else /* internal spin lock */ #if defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (__x86_64__) static inline void relax() { __asm volatile("rep;nop\n" ::: "memory"); } #else /* if no x86_64 or i586 arch: use less optimized but generic asm */ static inline void relax() { __asm volatile("" ::: "memory"); } #endif static inline void _shctx_wait4lock(unsigned int *count, unsigned int *uaddr, int value) { int i; for (i = 0; i < *count; i++) { relax(); relax(); if (*uaddr != value) return; } *count = (unsigned char)((*count << 1) + 1); } #define _shctx_awakelocker(a) #endif #if defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (__x86_64__) static inline unsigned int xchg(unsigned int *ptr, unsigned int x) { __asm volatile("lock xchgl %0,%1" : "=r" (x), "+m" (*ptr) : "0" (x) : "memory"); return x; } static inline unsigned int cmpxchg(unsigned int *ptr, unsigned int old, unsigned int new) { unsigned int ret; __asm volatile("lock cmpxchgl %2,%1" : "=a" (ret), "+m" (*ptr) : "r" (new), "0" (old) : "memory"); return ret; } static inline unsigned char atomic_dec(unsigned int *ptr) { unsigned char ret; __asm volatile("lock decl %0\n" "setne %1\n" : "+m" (*ptr), "=qm" (ret) : : "memory"); return ret; } #else /* if no x86_64 or i586 arch: use less optimized gcc >= 4.1 built-ins */ static inline unsigned int xchg(unsigned int *ptr, unsigned int x) { return __sync_lock_test_and_set(ptr, x); } static inline unsigned int cmpxchg(unsigned int *ptr, unsigned int old, unsigned int new) { return __sync_val_compare_and_swap(ptr, old, new); } static inline unsigned char atomic_dec(unsigned int *ptr) { return __sync_sub_and_fetch(ptr, 1) ? 1 : 0; } #endif static inline void _shctx_lock(struct shared_context *shctx) { unsigned int x; unsigned int count = 3; x = cmpxchg(&shctx->waiters, 0, 1); if (x) { if (x != 2) x = xchg(&shctx->waiters, 2); while (x) { _shctx_wait4lock(&count, &shctx->waiters, 2); x = xchg(&shctx->waiters, 2); } } } static inline void _shctx_unlock(struct shared_context *shctx) { if (atomic_dec(&shctx->waiters)) { shctx->waiters = 0; _shctx_awakelocker(&shctx->waiters); } } #define shctx_lock(shctx) if (use_shared_mem) _shctx_lock(shctx) #define shctx_unlock(shctx) if (use_shared_mem) _shctx_unlock(shctx) #endif /* List Macros */ /* * Insert block after which is not necessarily the head of a list, * so between and the next element after . */ static inline void shctx_block_append_hot(struct shared_context *shctx, struct list *head, struct shared_block *s) { shctx->nbav--; LIST_DEL(&s->list); LIST_ADD(head, &s->list); } static inline void shctx_block_set_hot(struct shared_context *shctx, struct shared_block *s) { shctx->nbav--; LIST_DEL(&s->list); LIST_ADDQ(&shctx->hot, &s->list); } static inline void shctx_block_set_avail(struct shared_context *shctx, struct shared_block *s) { shctx->nbav++; LIST_DEL(&s->list); LIST_ADDQ(&shctx->avail, &s->list); } #endif /* __HAPROXY_SHCTX_H */