Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * 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).
 *
 * GPL HEADER END
 */
/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Copyright (c) 2012, 2015 Intel Corporation.
 */
/*
 * This file is part of Lustre, http://www.lustre.org/
 * Lustre is a trademark of Sun Microsystems, Inc.
 *
 * libcfs/include/libcfs/libcfs_cpu.h
 *
 * CPU partition
 *   . CPU partition is virtual processing unit
 *
 *   . CPU partition can present 1-N cores, or 1-N NUMA nodes,
 *     in other words, CPU partition is a processors pool.
 *
 * CPU Partition Table (CPT)
 *   . a set of CPU partitions
 *
 *   . There are two modes for CPT: CFS_CPU_MODE_NUMA and CFS_CPU_MODE_SMP
 *
 *   . User can specify total number of CPU partitions while creating a
 *     CPT, ID of CPU partition is always start from 0.
 *
 *     Example: if there are 8 cores on the system, while creating a CPT
 *     with cpu_npartitions=4:
 *	      core[0, 1] = partition[0], core[2, 3] = partition[1]
 *	      core[4, 5] = partition[2], core[6, 7] = partition[3]
 *
 *	  cpu_npartitions=1:
 *	      core[0, 1, ... 7] = partition[0]
 *
 *   . User can also specify CPU partitions by string pattern
 *
 *     Examples: cpu_partitions="0[0,1], 1[2,3]"
 *	       cpu_partitions="N 0[0-3], 1[4-8]"
 *
 *     The first character "N" means following numbers are numa ID
 *
 *   . NUMA allocators, CPU affinity threads are built over CPU partitions,
 *     instead of HW CPUs or HW nodes.
 *
 *   . By default, Lustre modules should refer to the global cfs_cpt_table,
 *     instead of accessing HW CPUs directly, so concurrency of Lustre can be
 *     configured by cpu_npartitions of the global cfs_cpt_table
 *
 *   . If cpu_npartitions=1(all CPUs in one pool), lustre should work the
 *     same way as 2.2 or earlier versions
 *
 * Author: liang@whamcloud.com
 */

#ifndef __LIBCFS_CPU_H__
#define __LIBCFS_CPU_H__

/* any CPU partition */
#define CFS_CPT_ANY		(-1)

#ifdef CONFIG_SMP
/**
 * return cpumask of CPU partition \a cpt
 */
cpumask_t *cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt);
/**
 * print string information of cpt-table
 */
int cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len);
#else /* !CONFIG_SMP */
struct cfs_cpt_table {
	/* # of CPU partitions */
	int			ctb_nparts;
	/* cpu mask */
	cpumask_t		ctb_mask;
	/* node mask */
	nodemask_t		ctb_nodemask;
	/* version */
	u64			ctb_version;
};

static inline cpumask_t *
cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt)
{
	return NULL;
}

static inline int
cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
{
	return 0;
}
#endif /* CONFIG_SMP */

extern struct cfs_cpt_table	*cfs_cpt_table;

/**
 * destroy a CPU partition table
 */
void cfs_cpt_table_free(struct cfs_cpt_table *cptab);
/**
 * create a cfs_cpt_table with \a ncpt number of partitions
 */
struct cfs_cpt_table *cfs_cpt_table_alloc(unsigned int ncpt);
/**
 * return total number of CPU partitions in \a cptab
 */
int
cfs_cpt_number(struct cfs_cpt_table *cptab);
/**
 * return number of HW cores or hyper-threadings in a CPU partition \a cpt
 */
int cfs_cpt_weight(struct cfs_cpt_table *cptab, int cpt);
/**
 * is there any online CPU in CPU partition \a cpt
 */
int cfs_cpt_online(struct cfs_cpt_table *cptab, int cpt);
/**
 * return nodemask of CPU partition \a cpt
 */
nodemask_t *cfs_cpt_nodemask(struct cfs_cpt_table *cptab, int cpt);
/**
 * shadow current HW processor ID to CPU-partition ID of \a cptab
 */
int cfs_cpt_current(struct cfs_cpt_table *cptab, int remap);
/**
 * shadow HW processor ID \a CPU to CPU-partition ID by \a cptab
 */
int cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu);
/**
 * bind current thread on a CPU-partition \a cpt of \a cptab
 */
int cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt);
/**
 * add \a cpu to CPU partition @cpt of \a cptab, return 1 for success,
 * otherwise 0 is returned
 */
int cfs_cpt_set_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu);
/**
 * remove \a cpu from CPU partition \a cpt of \a cptab
 */
void cfs_cpt_unset_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu);
/**
 * add all cpus in \a mask to CPU partition \a cpt
 * return 1 if successfully set all CPUs, otherwise return 0
 */
int cfs_cpt_set_cpumask(struct cfs_cpt_table *cptab,
			int cpt, cpumask_t *mask);
/**
 * remove all cpus in \a mask from CPU partition \a cpt
 */
void cfs_cpt_unset_cpumask(struct cfs_cpt_table *cptab,
			   int cpt, cpumask_t *mask);
/**
 * add all cpus in NUMA node \a node to CPU partition \a cpt
 * return 1 if successfully set all CPUs, otherwise return 0
 */
int cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node);
/**
 * remove all cpus in NUMA node \a node from CPU partition \a cpt
 */
void cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node);

/**
 * add all cpus in node mask \a mask to CPU partition \a cpt
 * return 1 if successfully set all CPUs, otherwise return 0
 */
int cfs_cpt_set_nodemask(struct cfs_cpt_table *cptab,
			 int cpt, nodemask_t *mask);
/**
 * remove all cpus in node mask \a mask from CPU partition \a cpt
 */
void cfs_cpt_unset_nodemask(struct cfs_cpt_table *cptab,
			    int cpt, nodemask_t *mask);
/**
 * unset all cpus for CPU partition \a cpt
 */
void cfs_cpt_clear(struct cfs_cpt_table *cptab, int cpt);
/**
 * convert partition id \a cpt to numa node id, if there are more than one
 * nodes in this partition, it might return a different node id each time.
 */
int cfs_cpt_spread_node(struct cfs_cpt_table *cptab, int cpt);

/**
 * return number of HTs in the same core of \a cpu
 */
int cfs_cpu_ht_nsiblings(int cpu);

/*
 * allocate per-cpu-partition data, returned value is an array of pointers,
 * variable can be indexed by CPU ID.
 *	cptab != NULL: size of array is number of CPU partitions
 *	cptab == NULL: size of array is number of HW cores
 */
void *cfs_percpt_alloc(struct cfs_cpt_table *cptab, unsigned int size);
/*
 * destroy per-cpu-partition variable
 */
void cfs_percpt_free(void *vars);
int cfs_percpt_number(void *vars);

#define cfs_percpt_for_each(var, i, vars)		\
	for (i = 0; i < cfs_percpt_number(vars) &&	\
		((var) = (vars)[i]) != NULL; i++)

/*
 * percpu partition lock
 *
 * There are some use-cases like this in Lustre:
 * . each CPU partition has it's own private data which is frequently changed,
 *   and mostly by the local CPU partition.
 * . all CPU partitions share some global data, these data are rarely changed.
 *
 * LNet is typical example.
 * CPU partition lock is designed for this kind of use-cases:
 * . each CPU partition has it's own private lock
 * . change on private data just needs to take the private lock
 * . read on shared data just needs to take _any_ of private locks
 * . change on shared data needs to take _all_ private locks,
 *   which is slow and should be really rare.
 */
enum {
	CFS_PERCPT_LOCK_EX	= -1,	/* negative */
};

struct cfs_percpt_lock {
	/* cpu-partition-table for this lock */
	struct cfs_cpt_table     *pcl_cptab;
	/* exclusively locked */
	unsigned int		  pcl_locked;
	/* private lock table */
	spinlock_t		**pcl_locks;
};

/* return number of private locks */
#define cfs_percpt_lock_num(pcl)	cfs_cpt_number(pcl->pcl_cptab)

/*
 * create a cpu-partition lock based on CPU partition table \a cptab,
 * each private lock has extra \a psize bytes padding data
 */
struct cfs_percpt_lock *cfs_percpt_lock_create(struct cfs_cpt_table *cptab,
					       struct lock_class_key *keys);
/* destroy a cpu-partition lock */
void cfs_percpt_lock_free(struct cfs_percpt_lock *pcl);

/* lock private lock \a index of \a pcl */
void cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index);

/* unlock private lock \a index of \a pcl */
void cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index);

#define CFS_PERCPT_LOCK_KEYS	256

/* NB: don't allocate keys dynamically, lockdep needs them to be in ".data" */
#define cfs_percpt_lock_alloc(cptab)					\
({									\
	static struct lock_class_key ___keys[CFS_PERCPT_LOCK_KEYS];	\
	struct cfs_percpt_lock *___lk;					\
									\
	if (cfs_cpt_number(cptab) > CFS_PERCPT_LOCK_KEYS)		\
		___lk = cfs_percpt_lock_create(cptab, NULL);		\
	else								\
		___lk = cfs_percpt_lock_create(cptab, ___keys);		\
	___lk;								\
})

/**
 * iterate over all CPU partitions in \a cptab
 */
#define cfs_cpt_for_each(i, cptab)	\
	for (i = 0; i < cfs_cpt_number(cptab); i++)

int  cfs_cpu_init(void);
void cfs_cpu_fini(void);

#endif /* __LIBCFS_CPU_H__ */