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

📄 cmf.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
		 */		memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);		cmb_data->last_update = 0;	}	cdev->private->cmb_start_time = get_clock();	spin_unlock_irq(cdev->ccwlock);}/** * 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 * @num_channels: number of channels to be measured * @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(cmb_area.lock),	.list = LIST_HEAD_INIT(cmb_area.list),	.num_channels  = 1024,};/* ****** old style CMB handling ********//* * 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 * @ssch_rsch_count: number of ssch and rsch * @sample_count: number of samples * @device_connect_time: time of device connect * @function_pending_time: time of function pending * @device_disconnect_time: time of device disconnect * @control_unit_queuing_time: time of control unit queuing * @device_active_only_time: time of device active only * @reserved: unused in basic measurement mode * * The measurement block as used by the hardware. The fields are described * further in z/Architecture Principles of Operation, chapter 17. * * The cmb area made up from these blocks must 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 int alloc_cmb_single(struct ccw_device *cdev,			    struct cmb_data *cmb_data){	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->hw_data pointers.	 */	cmb = cmb_area.mem;	list_for_each_entry(node, &cmb_area.list, cmb_list) {		struct cmb_data *data;		data = node->cmb;		if ((struct cmb*)data->hw_block > 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);	cmb_data->hw_block = cmb;	cdev->private->cmb = cmb_data;	ret = 0;out:	spin_unlock_irq(cdev->ccwlock);	return ret;}static int alloc_cmb(struct ccw_device *cdev){	int ret;	struct cmb *mem;	ssize_t size;	struct cmb_data *cmb_data;	/* Allocate private cmb_data. */	cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);	if (!cmb_data)		return -ENOMEM;	cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL);	if (!cmb_data->last_block) {		kfree(cmb_data);		return -ENOMEM;	}	cmb_data->size = sizeof(struct cmb);	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 */			printk(KERN_WARNING "cio: failed to allocate area "			       "for measuring %d subchannels\n",			       cmb_area.num_channels);			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, cmb_data);out:	spin_unlock(&cmb_area.lock);	if (ret) {		kfree(cmb_data->last_block);		kfree(cmb_data);	}	return ret;}static void free_cmb(struct ccw_device *cdev){	struct ccw_device_private *priv;	struct cmb_data *cmb_data;	spin_lock(&cmb_area.lock);	spin_lock_irq(cdev->ccwlock);	priv = cdev->private;	if (list_empty(&priv->cmb_list)) {		/* already freed */		goto out;	}	cmb_data = priv->cmb;	priv->cmb = NULL;	if (cmb_data)		kfree(cmb_data->last_block);	kfree(cmb_data);	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 int set_cmb(struct ccw_device *cdev, u32 mme){	u16 offset;	struct cmb_data *cmb_data;	unsigned long flags;	spin_lock_irqsave(cdev->ccwlock, flags);	if (!cdev->private->cmb) {		spin_unlock_irqrestore(cdev->ccwlock, flags);		return -EINVAL;	}	cmb_data = cdev->private->cmb;	offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0;	spin_unlock_irqrestore(cdev->ccwlock, flags);	return set_schib_wait(cdev, mme, 0, offset);}static u64 read_cmb(struct ccw_device *cdev, int index){	struct cmb *cmb;	u32 val;	int ret;	unsigned long flags;	ret = cmf_cmb_copy_wait(cdev);	if (ret < 0)		return 0;	spin_lock_irqsave(cdev->ccwlock, flags);	if (!cdev->private->cmb) {		ret = 0;		goto out;	}	cmb = ((struct cmb_data *)cdev->private->cmb)->last_block;	switch (index) {	case cmb_ssch_rsch_count:		ret = cmb->ssch_rsch_count;		goto out;	case cmb_sample_count:		ret = cmb->sample_count;		goto out;	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:		ret = 0;		goto out;	}	ret = time_to_avg_nsec(val, cmb->sample_count);out:	spin_unlock_irqrestore(cdev->ccwlock, flags);	return ret;}static int readall_cmb(struct ccw_device *cdev, struct cmbdata *data){	struct cmb *cmb;	struct cmb_data *cmb_data;	u64 time;	unsigned long flags;	int ret;	ret = cmf_cmb_copy_wait(cdev);	if (ret < 0)		return ret;	spin_lock_irqsave(cdev->ccwlock, flags);	cmb_data = cdev->private->cmb;	if (!cmb_data) {		ret = -ENODEV;		goto out;	}	if (cmb_data->last_update == 0) {		ret = -EAGAIN;		goto out;	}	cmb = cmb_data->last_block;	time = cmb_data->last_update - cdev->private->cmb_start_time;	memset(data, 0, sizeof(struct cmbdata));	/* we only know values before device_busy_time */	data->size = offsetof(struct cmbdata, device_busy_time);	/* convert to nanoseconds */	data->elapsed_time = (time * 1000) >> 12;	/* copy data to new structure */	data->ssch_rsch_count = cmb->ssch_rsch_count;	data->sample_count = cmb->sample_count;	/* time fields are converted to nanoseconds while copying */	data->device_connect_time = time_to_nsec(cmb->device_connect_time);	data->function_pending_time = time_to_nsec(cmb->function_pending_time);	data->device_disconnect_time =		time_to_nsec(cmb->device_disconnect_time);	data->control_unit_queuing_time		= time_to_nsec(cmb->control_unit_queuing_time);	data->device_active_only_time		= time_to_nsec(cmb->device_active_only_time);	ret = 0;out:	spin_unlock_irqrestore(cdev->ccwlock, flags);	return ret;}static void reset_cmb(struct ccw_device *cdev){	cmf_generic_reset(cdev);}static void * align_cmb(void *area){	return area;}static struct attribute_group cmf_attr_group;static struct cmb_operations cmbops_basic = {	.alloc	= alloc_cmb,	.free	= free_cmb,	.set	= set_cmb,	.read	= read_cmb,	.readall    = readall_cmb,	.reset	    = reset_cmb,	.align	    = align_cmb,	.attr_group = &cmf_attr_group,};/* ******** extended cmb handling ********//** * struct cmbe - extended channel measurement block * @ssch_rsch_count: number of ssch and rsch * @sample_count: number of samples * @device_connect_time: time of device connect * @function_pending_time: time of function pending * @device_disconnect_time: time of device disconnect * @control_unit_queuing_time: time of control unit queuing * @device_active_only_time: time of device active only * @device_busy_time: time of device busy * @initial_command_response_time: initial command response time * @reserved: unused * * The measurement block as used by the hardware. May be in any 64 bit physical * location. * The fields are described further in z/Architecture Principles of Operation, * third edition, chapter 17. */struct cmbe {	u32 ssch_rsch_count;	u32 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 device_busy_time;	u32 initial_command_response_time;	u32 reserved[7];};/* * kmalloc only guarantees 8 byte alignment, but we need cmbe * pointers to be naturally aligned. Make sure to allocate * enough space for two cmbes. */static inline struct cmbe *cmbe_align(struct cmbe *c){	unsigned long addr;	addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) &				 ~(sizeof (struct cmbe) - sizeof(long));	return (struct cmbe*)addr;}static int alloc_cmbe(struct ccw_device *cdev){	struct cmbe *cmbe;	struct cmb_data *cmb_data;	int ret;	cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);	if (!cmbe)		return -ENOMEM;	cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);	if (!cmb_data) {		ret = -ENOMEM;		goto out_free;	}	cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);	if (!cmb_data->last_block) {		ret = -ENOMEM;		goto out_free;	}	cmb_data->size = sizeof(struct cmbe);	spin_lock_irq(cdev->ccwlock);	if (cdev->private->cmb) {		spin_unlock_irq(cdev->ccwlock);		ret = -EBUSY;		goto out_free;	}	cmb_data->hw_block = cmbe;	cdev->private->cmb = cmb_data;	spin_unlock_irq(cdev->ccwlock);	/* activate global measurement if this is the first channel */	spin_lock(&cmb_area.lock);	if (list_empty(&cmb_area.list))		cmf_activate(NULL, 1);	list_add_tail(&cdev->private->cmb_list, &cmb_area.list);	spin_unlock(&cmb_area.lock);	return 0;out_free:	if (cmb_data)		kfree(cmb_data->last_block);	kfree(cmb_data);	kfree(cmbe);	return ret;}static void free_cmbe(struct ccw_device *cdev){	struct cmb_data *cmb_data;	spin_lock_irq(cdev->ccwlock);	cmb_data = cdev->private->cmb;	cdev->private->cmb = NULL;	if (cmb_data)		kfree(cmb_data->last_block);	kfree(cmb_data);	spin_unlock_irq(cdev->ccwlock);	/* deactivate global measurement if this is the last channel */	spin_lock(&cmb_area.lock);	list_del_init(&cdev->private->cmb_list);	if (list_empty(&cmb_area.list))		cmf_activate(NULL, 0);	spin_unlock(&cmb_area.lock);

⌨️ 快捷键说明

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