/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is included * in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA * * GPL HEADER END */ /* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2012, Whamcloud, Inc. */ /* * This file is part of Lustre, http://www.lustre.org/ * Lustre is a trademark of Sun Microsystems, Inc. * * Author: liang@whamcloud.com */ #ifndef EXPORT_SYMTAB # define EXPORT_SYMTAB #endif #define DEBUG_SUBSYSTEM S_LNET #include #ifdef __KERNEL__ /** destroy cpu-partition lock, see libcfs_private.h for more detail */ void cfs_percpt_lock_free(struct cfs_percpt_lock *pcl) { LASSERT(pcl->pcl_locks != NULL); LASSERT(!pcl->pcl_locked); cfs_percpt_free(pcl->pcl_locks); LIBCFS_FREE(pcl, sizeof(*pcl)); } CFS_EXPORT_SYMBOL(cfs_percpt_lock_free); /** * create cpu-partition lock, see libcfs_private.h for more detail. * * cpu-partition lock is designed for large-scale SMP system, so we need to * reduce cacheline conflict as possible as we can, that's the * reason we always allocate cacheline-aligned memory block. */ struct cfs_percpt_lock * cfs_percpt_lock_alloc(struct cfs_cpt_table *cptab) { struct cfs_percpt_lock *pcl; cfs_spinlock_t *lock; int i; /* NB: cptab can be NULL, pcl will be for HW CPUs on that case */ LIBCFS_ALLOC(pcl, sizeof(*pcl)); if (pcl == NULL) return NULL; pcl->pcl_cptab = cptab; pcl->pcl_locks = cfs_percpt_alloc(cptab, sizeof(*lock)); if (pcl->pcl_locks == NULL) { LIBCFS_FREE(pcl, sizeof(*pcl)); return NULL; } cfs_percpt_for_each(lock, i, pcl->pcl_locks) cfs_spin_lock_init(lock); return pcl; } CFS_EXPORT_SYMBOL(cfs_percpt_lock_alloc); /** * lock a CPU partition * * \a index != CFS_PERCPT_LOCK_EX * hold private lock indexed by \a index * * \a index == CFS_PERCPT_LOCK_EX * exclusively lock @pcl and nobody can take private lock */ void cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index) { int ncpt = cfs_cpt_number(pcl->pcl_cptab); int i; LASSERT(index >= CFS_PERCPT_LOCK_EX && index < ncpt); if (ncpt == 1) { index = 0; } else { /* serialize with exclusive lock */ while (pcl->pcl_locked) cpu_relax(); } if (likely(index != CFS_PERCPT_LOCK_EX)) { cfs_spin_lock(pcl->pcl_locks[index]); return; } /* exclusive lock request */ for (i = 0; i < ncpt; i++) { cfs_spin_lock(pcl->pcl_locks[i]); if (i == 0) { LASSERT(!pcl->pcl_locked); /* nobody should take private lock after this * so I wouldn't starve for too long time */ pcl->pcl_locked = 1; } } } CFS_EXPORT_SYMBOL(cfs_percpt_lock); /** unlock a CPU partition */ void cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index) { int ncpt = cfs_cpt_number(pcl->pcl_cptab); int i; index = ncpt == 1 ? 0 : index; if (likely(index != CFS_PERCPT_LOCK_EX)) { cfs_spin_unlock(pcl->pcl_locks[index]); return; } for (i = ncpt - 1; i >= 0; i--) { if (i == 0) { LASSERT(pcl->pcl_locked); pcl->pcl_locked = 0; } cfs_spin_unlock(pcl->pcl_locks[i]); } } CFS_EXPORT_SYMBOL(cfs_percpt_unlock); #else /* !__KERNEL__ */ # ifdef HAVE_LIBPTHREAD struct cfs_percpt_lock * cfs_percpt_lock_alloc(struct cfs_cpt_table *cptab) { struct cfs_percpt_lock *pcl; CFS_ALLOC_PTR(pcl); if (pcl != NULL) pthread_mutex_init(&pcl->pcl_mutex, NULL); return pcl; } void cfs_percpt_lock_free(struct cfs_percpt_lock *pcl) { pthread_mutex_destroy(&pcl->pcl_mutex); CFS_FREE_PTR(pcl); } void cfs_percpt_lock(struct cfs_percpt_lock *pcl, int lock) { pthread_mutex_lock(&(pcl)->pcl_mutex); } void cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int lock) { pthread_mutex_unlock(&(pcl)->pcl_mutex); } # else /* !HAVE_LIBPTHREAD */ struct cfs_percpt_lock * cfs_percpt_lock_alloc(struct cfs_cpt_table *cptab) { return ((struct cfs_percpt_lock *) &CFS_PERCPT_LOCK_MAGIC); } void cfs_percpt_lock_free(struct cfs_percpt_lock *pcl) { LASSERT(pcl == (struct cfs_percpt_lock *) &CFS_PERCPT_LOCK_MAGIC); } void cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index) { LASSERT(pcl == (struct cfs_percpt_lock *) &CFS_PERCPT_LOCK_MAGIC); } void cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index) { LASSERT(pcl == (struct cfs_percpt_lock *) &CFS_PERCPT_LOCK_MAGIC); } # endif /* HAVE_LIBPTHREAD */ #endif /* __KERNEL__ */ /** free cpu-partition refcount */ void cfs_percpt_atomic_free(cfs_atomic_t **refs) { cfs_percpt_free(refs); } CFS_EXPORT_SYMBOL(cfs_percpt_atomic_free); /** allocate cpu-partition refcount with initial value @init_val */ cfs_atomic_t ** cfs_percpt_atomic_alloc(struct cfs_cpt_table *cptab, int init_val) { cfs_atomic_t **refs; cfs_atomic_t *ref; int i; refs = cfs_percpt_alloc(cptab, sizeof(*ref)); if (refs == NULL) return NULL; cfs_percpt_for_each(ref, i, refs) cfs_atomic_set(ref, init_val); return refs; } CFS_EXPORT_SYMBOL(cfs_percpt_atomic_alloc); /** return sum of cpu-partition refs */ int cfs_percpt_atomic_summary(cfs_atomic_t **refs) { cfs_atomic_t *ref; int i; int val = 0; cfs_percpt_for_each(ref, i, refs) val += cfs_atomic_read(ref); return val; } CFS_EXPORT_SYMBOL(cfs_percpt_atomic_summary);