📄 cmf.c
字号:
*/ 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 + -