⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmf.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/drivers/s390/cio/cmf.c ($Revision: 1.16 $) * * Linux on zSeries Channel Measurement Facility support * * Copyright 2000,2003 IBM Corporation * * Author: Arnd Bergmann <arndb@de.ibm.com> * * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> * * 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, or (at your option) * any later version. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/bootmem.h>#include <linux/device.h>#include <linux/init.h>#include <linux/list.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/timex.h>	/* get_clock() */#include <asm/ccwdev.h>#include <asm/cio.h>#include <asm/cmb.h>#include <asm/div64.h>#include "cio.h"#include "css.h"#include "device.h"#include "ioasm.h"#include "chsc.h"/* parameter to enable cmf during boot, possible uses are: *  "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be *               used on any subchannel *  "s390cmf=<num>" -- enable cmf and allocate enough memory to measure *                     <num> subchannel, where <num> is an integer *                     between 1 and 65535, default is 1024 */#define ARGSTRING "s390cmf"/* indices for READCMB */enum cmb_index { /* basic and exended format: */	cmb_ssch_rsch_count,	cmb_sample_count,	cmb_device_connect_time,	cmb_function_pending_time,	cmb_device_disconnect_time,	cmb_control_unit_queuing_time,	cmb_device_active_only_time, /* extended format only: */	cmb_device_busy_time,	cmb_initial_command_response_time,};/** * enum cmb_format - types of supported measurement block formats * * @CMF_BASIC:      traditional channel measurement blocks supported * 		    by all machines that we run on * @CMF_EXTENDED:   improved format that was introduced with the z990 * 		    machine * @CMF_AUTODETECT: default: use extended format when running on a z990 *                  or later machine, otherwise fall back to basic format **/enum cmb_format {	CMF_BASIC,	CMF_EXTENDED,	CMF_AUTODETECT = -1,};/** * format - actual format for all measurement blocks * * The format module parameter can be set to a value of 0 (zero) * or 1, indicating basic or extended format as described for * enum cmb_format. */static int format = CMF_AUTODETECT;module_param(format, bool, 0444);/** * struct cmb_operations - functions to use depending on cmb_format * * all these functions operate on a struct cmf_device. There is only * one instance of struct cmb_operations because all cmf_device * objects are guaranteed to be of the same type. * * @alloc:	allocate memory for a channel measurement block, *		either with the help of a special pool or with kmalloc * @free:	free memory allocated with @alloc * @set:	enable or disable measurement * @readall:	read a measurement block in a common format * @reset:	clear the data in the associated measurement block and *		reset its time stamp */struct cmb_operations {	int (*alloc)  (struct ccw_device*);	void(*free)   (struct ccw_device*);	int (*set)    (struct ccw_device*, u32);	u64 (*read)   (struct ccw_device*, int);	int (*readall)(struct ccw_device*, struct cmbdata *);	void (*reset) (struct ccw_device*);	struct attribute_group *attr_group;};static struct cmb_operations *cmbops;/* our user interface is designed in terms of nanoseconds, * while the hardware measures total times in its own * unit.*/static inline u64 time_to_nsec(u32 value){	return ((u64)value) * 128000ull;}/* * Users are usually interested in average times, * not accumulated time. * This also helps us with atomicity problems * when reading sinlge values. */static inline u64 time_to_avg_nsec(u32 value, u32 count){	u64 ret;	/* no samples yet, avoid division by 0 */	if (count == 0)		return 0;	/* value comes in units of 128 祍ec */	ret = time_to_nsec(value);	do_div(ret, count);	return ret;}/* activate or deactivate the channel monitor. When area is NULL, * the monitor is deactivated. The channel monitor needs to * be active in order to measure subchannels, which also need * to be enabled. */static inline voidcmf_activate(void *area, unsigned int onoff){	register void * __gpr2 asm("2");	register long __gpr1 asm("1");	__gpr2 = area;	__gpr1 = onoff ? 2 : 0;	/* activate channel measurement */	asm("schm" : : "d" (__gpr2), "d" (__gpr1) );}static intset_schib(struct ccw_device *cdev, u32 mme, int mbfc, unsigned long address){	int ret;	int retry;	struct subchannel *sch;	struct schib *schib;	sch = to_subchannel(cdev->dev.parent);	schib = &sch->schib;	/* msch can silently fail, so do it again if necessary */	for (retry = 0; retry < 3; retry++) {		/* prepare schib */		stsch(sch->irq, schib);		schib->pmcw.mme  = mme;		schib->pmcw.mbfc = mbfc;		/* address can be either a block address or a block index */		if (mbfc)			schib->mba = address;		else			schib->pmcw.mbi = address;		/* try to submit it */		switch(ret = msch_err(sch->irq, schib)) {			case 0:				break;			case 1:			case 2: /* in I/O or status pending */				ret = -EBUSY;				break;			case 3: /* subchannel is no longer valid */				ret = -ENODEV;				break;			default: /* msch caught an exception */				ret = -EINVAL;				break;		}		stsch(sch->irq, schib); /* restore the schib */		if (ret)			break;		/* check if it worked */		if (schib->pmcw.mme  == mme &&		    schib->pmcw.mbfc == mbfc &&		    (mbfc ? (schib->mba == address)			  : (schib->pmcw.mbi == address)))			return 0;		ret = -EINVAL;	}	return ret;}struct set_schib_struct {	u32 mme;	int mbfc;	unsigned long address;	wait_queue_head_t wait;	int ret;};static int set_schib_wait(struct ccw_device *cdev, u32 mme,				int mbfc, unsigned long address){	struct set_schib_struct s = {		.mme = mme,		.mbfc = mbfc,		.address = address,		.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait),	};	spin_lock_irq(cdev->ccwlock);	s.ret = set_schib(cdev, mme, mbfc, address);	if (s.ret != -EBUSY) {		goto out_nowait;	}	if (cdev->private->state != DEV_STATE_ONLINE) {		s.ret = -EBUSY;		/* if the device is not online, don't even try again */		goto out_nowait;	}	cdev->private->state = DEV_STATE_CMFCHANGE;	cdev->private->cmb_wait = &s;	s.ret = 1;	spin_unlock_irq(cdev->ccwlock);	if (wait_event_interruptible(s.wait, s.ret != 1)) {		spin_lock_irq(cdev->ccwlock);		if (s.ret == 1) {			s.ret = -ERESTARTSYS;			cdev->private->cmb_wait = 0;			if (cdev->private->state == DEV_STATE_CMFCHANGE)				cdev->private->state = DEV_STATE_ONLINE;		}		spin_unlock_irq(cdev->ccwlock);	}	return s.ret;out_nowait:	spin_unlock_irq(cdev->ccwlock);	return s.ret;}void retry_set_schib(struct ccw_device *cdev){	struct set_schib_struct *s;	s = cdev->private->cmb_wait;	cdev->private->cmb_wait = 0;	if (!s) {		WARN_ON(1);		return;	}	s->ret = set_schib(cdev, s->mme, s->mbfc, s->address);	wake_up(&s->wait);}/** * struct cmb_area - container for global cmb data * * @mem:	pointer to CMBs (only in basic measurement mode) * @list:	contains a linked list of all subchannels * @lock:	protect concurrent access to @mem and @list */struct cmb_area {	struct cmb *mem;	struct list_head list;	int num_channels;	spinlock_t lock;};static struct cmb_area cmb_area = {	.lock = SPIN_LOCK_UNLOCKED,	.list = LIST_HEAD_INIT(cmb_area.list),	.num_channels  = 1024,};/* ****** old style CMB handling ********//** int maxchannels * * Basic channel measurement blocks are allocated in one contiguous * block of memory, which can not be moved as long as any channel * is active. Therefore, a maximum number of subchannels needs to * be defined somewhere. This is a module parameter, defaulting to * a resonable value of 1024, or 32 kb of memory. * Current kernels don't allow kmalloc with more than 128kb, so the * maximum is 4096 */module_param_named(maxchannels, cmb_area.num_channels, uint, 0444);/** * struct cmb - basic channel measurement block * * cmb as used by the hardware the fields are described in z/Architecture * Principles of Operation, chapter 17. * The area to be a contiguous array and may not be reallocated or freed. * Only one cmb area can be present in the system. */struct cmb {	u16 ssch_rsch_count;	u16 sample_count;	u32 device_connect_time;	u32 function_pending_time;	u32 device_disconnect_time;	u32 control_unit_queuing_time;	u32 device_active_only_time;	u32 reserved[2];};/* insert a single device into the cmb_area list * called with cmb_area.lock held from alloc_cmb */static inline intalloc_cmb_single (struct ccw_device *cdev){	struct cmb *cmb;	struct ccw_device_private *node;	int ret;	spin_lock_irq(cdev->ccwlock);	if (!list_empty(&cdev->private->cmb_list)) {		ret = -EBUSY;		goto out;	}	/* find first unused cmb in cmb_area.mem.	 * this is a little tricky: cmb_area.list	 * remains sorted by ->cmb pointers */	cmb = cmb_area.mem;	list_for_each_entry(node, &cmb_area.list, cmb_list) {		if ((struct cmb*)node->cmb > cmb)			break;		cmb++;	}	if (cmb - cmb_area.mem >= cmb_area.num_channels) {		ret = -ENOMEM;		goto out;	}	/* insert new cmb */	list_add_tail(&cdev->private->cmb_list, &node->cmb_list);	cdev->private->cmb = cmb;	ret = 0;out:	spin_unlock_irq(cdev->ccwlock);	return ret;}static intalloc_cmb (struct ccw_device *cdev){	int ret;	struct cmb *mem;	ssize_t size;	spin_lock(&cmb_area.lock);	if (!cmb_area.mem) {		/* there is no user yet, so we need a new area */		size = sizeof(struct cmb) * cmb_area.num_channels;		WARN_ON(!list_empty(&cmb_area.list));		spin_unlock(&cmb_area.lock);		mem = (void*)__get_free_pages(GFP_KERNEL | GFP_DMA,				 get_order(size));		spin_lock(&cmb_area.lock);		if (cmb_area.mem) {			/* ok, another thread was faster */			free_pages((unsigned long)mem, get_order(size));		} else if (!mem) {			/* no luck */			ret = -ENOMEM;			goto out;		} else {			/* everything ok */			memset(mem, 0, size);			cmb_area.mem = mem;			cmf_activate(cmb_area.mem, 1);		}	}	/* do the actual allocation */	ret = alloc_cmb_single(cdev);out:	spin_unlock(&cmb_area.lock);	return ret;}static voidfree_cmb(struct ccw_device *cdev){	struct ccw_device_private *priv;	priv = cdev->private;	spin_lock(&cmb_area.lock);	spin_lock_irq(cdev->ccwlock);	if (list_empty(&priv->cmb_list)) {		/* already freed */		goto out;	}	priv->cmb = NULL;	list_del_init(&priv->cmb_list);	if (list_empty(&cmb_area.list)) {		ssize_t size;		size = sizeof(struct cmb) * cmb_area.num_channels;		cmf_activate(NULL, 0);		free_pages((unsigned long)cmb_area.mem, get_order(size));		cmb_area.mem = NULL;	}out:	spin_unlock_irq(cdev->ccwlock);	spin_unlock(&cmb_area.lock);}static intset_cmb(struct ccw_device *cdev, u32 mme){	u16 offset;	if (!cdev->private->cmb)		return -EINVAL;	offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0;	return set_schib_wait(cdev, mme, 0, offset);}static u64read_cmb (struct ccw_device *cdev, int index){	/* yes, we have to put it on the stack	 * because the cmb must only be accessed	 * atomically, e.g. with mvc */	struct cmb cmb;	unsigned long flags;	u32 val;	spin_lock_irqsave(cdev->ccwlock, flags);	if (!cdev->private->cmb) {		spin_unlock_irqrestore(cdev->ccwlock, flags);		return 0;	}	cmb = *(struct cmb*)cdev->private->cmb;	spin_unlock_irqrestore(cdev->ccwlock, flags);	switch (index) {	case cmb_ssch_rsch_count:		return cmb.ssch_rsch_count;	case cmb_sample_count:		return cmb.sample_count;	case cmb_device_connect_time:		val = cmb.device_connect_time;		break;	case cmb_function_pending_time:		val = cmb.function_pending_time;		break;	case cmb_device_disconnect_time:		val = cmb.device_disconnect_time;		break;	case cmb_control_unit_queuing_time:		val = cmb.control_unit_queuing_time;		break;	case cmb_device_active_only_time:		val = cmb.device_active_only_time;		break;	default:		return 0;	}	return time_to_avg_nsec(val, cmb.sample_count);}static intreadall_cmb (struct ccw_device *cdev, struct cmbdata *data){	/* yes, we have to put it on the stack	 * because the cmb must only be accessed	 * atomically, e.g. with mvc */	struct cmb cmb;	unsigned long flags;	u64 time;	spin_lock_irqsave(cdev->ccwlock, flags);	if (!cdev->private->cmb) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -