/* * debug.h -- debugging routines for libtirpc * * Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CLNT_FD_LOCKS_H #define _CLNT_FD_LOCKS_H #include #include #include #include /* * This utility manages a list of per-fd locks for the clients. * * If MAX_FDLOCKS_PREALLOC is defined, a number of pre-fd locks will be * pre-allocated. This number is the minimum of MAX_FDLOCKS_PREALLOC or * the process soft limit of allowed fds. */ #ifdef MAX_FDLOCKS_PREALLOC static unsigned int fd_locks_prealloc = 0; #endif /* per-fd lock */ struct fd_lock_t { bool_t active; int pending; /* Number of pending operations on fd */ cond_t cv; }; typedef struct fd_lock_t fd_lock_t; /* internal type to store per-fd locks in a list */ struct fd_lock_item_t { /* fd_lock_t first so we can cast to fd_lock_item_t */ fd_lock_t fd_lock; int fd; unsigned int refs; TAILQ_ENTRY(fd_lock_item_t) link; }; typedef struct fd_lock_item_t fd_lock_item_t; #define to_fd_lock_item(fdlock_t_ptr) ((fd_lock_item_t*) fdlock_t_ptr) /* internal list of per-fd locks */ typedef TAILQ_HEAD(,fd_lock_item_t) fd_lock_list_t; #ifdef MAX_FDLOCKS_PREALLOC /* With pre-allocation, keep track of both an array and a list */ struct fd_locks_t { fd_lock_list_t fd_lock_list; fd_lock_t *fd_lock_array; }; typedef struct fd_locks_t fd_locks_t; #define to_fd_lock_list(fd_locks_t_ptr) (&fd_locks_t_ptr->fd_lock_list) #else /* With no pre-allocation, just keep track of a list */ typedef fd_lock_list_t fd_locks_t; #define to_fd_lock_list(fd_locks_t_ptr) ((fd_lock_list_t *) fd_locks_t_ptr) #endif /* allocate fd locks */ static inline fd_locks_t* fd_locks_init() { fd_locks_t *fd_locks; fd_locks = (fd_locks_t *) mem_alloc(sizeof(fd_locks_t)); if (fd_locks == (fd_locks_t *) NULL) { errno = ENOMEM; return (NULL); } TAILQ_INIT(to_fd_lock_list(fd_locks)); #ifdef MAX_FDLOCKS_PREALLOC size_t fd_lock_arraysz; if (fd_locks_prealloc == 0) { unsigned int dtbsize = __rpc_dtbsize(); if (0 < dtbsize && dtbsize < MAX_FDLOCKS_PREALLOC) fd_locks_prealloc = dtbsize; else fd_locks_prealloc = MAX_FDLOCKS_PREALLOC; } if ( (size_t) fd_locks_prealloc > SIZE_MAX/sizeof(fd_lock_t)) { mem_free(fd_locks, sizeof (*fd_locks)); errno = EOVERFLOW; return (NULL); } fd_lock_arraysz = fd_locks_prealloc * sizeof (fd_lock_t); fd_locks->fd_lock_array = (fd_lock_t *) mem_alloc(fd_lock_arraysz); if (fd_locks->fd_lock_array == (fd_lock_t *) NULL) { mem_free(fd_locks, sizeof (*fd_locks)); errno = ENOMEM; return (NULL); } else { int i; for (i = 0; i < fd_locks_prealloc; i++) { fd_locks->fd_lock_array[i].active = FALSE; cond_init(&fd_locks->fd_lock_array[i].cv, 0, (void *) 0); } } #endif return fd_locks; } /* de-allocate fd locks */ static inline void fd_locks_destroy(fd_locks_t *fd_locks) { #ifdef MAX_FDLOCKS_PREALLOC fd_lock_t *array = fd_locks->fd_lock_array; mem_free(array, fd_locks_prealloc * sizeof (fd_lock_t)); #endif fd_lock_item_t *item; fd_lock_list_t *list = to_fd_lock_list(fd_locks); TAILQ_FOREACH(item, list, link) { cond_destroy(&item->fd_lock.cv); mem_free(item, sizeof (*item)); } mem_free(fd_locks, sizeof (*fd_locks)); } /* allocate per-fd lock */ static inline fd_lock_t* fd_lock_create(int fd, fd_locks_t *fd_locks) { #ifdef MAX_FDLOCKS_PREALLOC if (fd < fd_locks_prealloc) { return &fd_locks->fd_lock_array[fd]; } #endif fd_lock_item_t *item; fd_lock_list_t *list = to_fd_lock_list(fd_locks); for (item = TAILQ_FIRST(list); item != (fd_lock_item_t *) NULL && item->fd != fd; item = TAILQ_NEXT(item, link)); if (item == (fd_lock_item_t *) NULL) { item = (fd_lock_item_t *) mem_alloc(sizeof(fd_lock_item_t)); if (item == (fd_lock_item_t *) NULL) { errno = ENOMEM; return (NULL); } item->fd = fd; item->refs = 1; item->fd_lock.active = FALSE; item->fd_lock.pending = 0; cond_init(&item->fd_lock.cv, 0, (void *) 0); TAILQ_INSERT_HEAD(list, item, link); } else { item->refs++; } return &item->fd_lock; } /* de-allocate per-fd lock */ static inline void fd_lock_destroy(int fd, fd_lock_t *fd_lock, fd_locks_t *fd_locks) { #ifdef MAX_FDLOCKS_PREALLOC if (fd < fd_locks_prealloc) return; #endif fd_lock_item_t* item = to_fd_lock_item(fd_lock); item->refs--; if (item->refs <= 0) { TAILQ_REMOVE(to_fd_lock_list(fd_locks), item, link); cond_destroy(&item->fd_lock.cv); mem_free(item, sizeof (*item)); } } #endif /* _CLNT_FD_LOCKS_H */