X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fs390%2Fcio%2Fcmf.c;fp=drivers%2Fs390%2Fcio%2Fcmf.c;h=07ef3f640f4aa73ec17460a93ab020f21a9bfb2e;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=828b2d334f0a30fde8a687b7ac5587927151aa6f;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 828b2d334..07ef3f640 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -3,10 +3,9 @@ * * Linux on zSeries Channel Measurement Facility support * - * Copyright 2000,2006 IBM Corporation + * Copyright 2000,2003 IBM Corporation * - * Authors: Arnd Bergmann - * Cornelia Huck + * Author: Arnd Bergmann * * original idea from Natarajan Krishnaswami * @@ -97,9 +96,9 @@ module_param(format, bool, 0444); /** * struct cmb_operations - functions to use depending on cmb_format * - * Most of these functions operate on a struct ccw_device. There is only - * one instance of struct cmb_operations because the format of the measurement - * data is guaranteed to be the same for every ccw_device. + * 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 @@ -108,7 +107,6 @@ module_param(format, bool, 0444); * @readall: read a measurement block in a common format * @reset: clear the data in the associated measurement block and * reset its time stamp - * @align: align an allocated block so that the hardware can use it */ struct cmb_operations { int (*alloc) (struct ccw_device*); @@ -117,19 +115,11 @@ struct cmb_operations { u64 (*read) (struct ccw_device*, int); int (*readall)(struct ccw_device*, struct cmbdata *); void (*reset) (struct ccw_device*); - void * (*align) (void *); struct attribute_group *attr_group; }; static struct cmb_operations *cmbops; -struct cmb_data { - void *hw_block; /* Pointer to block updated by hardware */ - void *last_block; /* Last changed block copied from hardware block */ - int size; /* Size of hw_block and last_block */ - unsigned long long last_update; /* when last_block was updated */ -}; - /* our user interface is designed in terms of nanoseconds, * while the hardware measures total times in its own * unit.*/ @@ -236,229 +226,63 @@ struct set_schib_struct { unsigned long address; wait_queue_head_t wait; int ret; - struct kref kref; }; -static void cmf_set_schib_release(struct kref *kref) -{ - struct set_schib_struct *set_data; - - set_data = container_of(kref, struct set_schib_struct, kref); - kfree(set_data); -} - -#define CMF_PENDING 1 - static int set_schib_wait(struct ccw_device *cdev, u32 mme, int mbfc, unsigned long address) { - struct set_schib_struct *set_data; - int ret; + struct set_schib_struct s = { + .mme = mme, + .mbfc = mbfc, + .address = address, + .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait), + }; spin_lock_irq(cdev->ccwlock); - if (!cdev->private->cmb) { - ret = -ENODEV; - goto out; + s.ret = set_schib(cdev, mme, mbfc, address); + if (s.ret != -EBUSY) { + goto out_nowait; } - set_data = kzalloc(sizeof(struct set_schib_struct), GFP_ATOMIC); - if (!set_data) { - ret = -ENOMEM; - goto out; - } - init_waitqueue_head(&set_data->wait); - kref_init(&set_data->kref); - set_data->mme = mme; - set_data->mbfc = mbfc; - set_data->address = address; - - ret = set_schib(cdev, mme, mbfc, address); - if (ret != -EBUSY) - goto out_put; if (cdev->private->state != DEV_STATE_ONLINE) { + s.ret = -EBUSY; /* if the device is not online, don't even try again */ - ret = -EBUSY; - goto out_put; + goto out_nowait; } - cdev->private->state = DEV_STATE_CMFCHANGE; - set_data->ret = CMF_PENDING; - cdev->private->cmb_wait = set_data; + cdev->private->cmb_wait = &s; + s.ret = 1; spin_unlock_irq(cdev->ccwlock); - if (wait_event_interruptible(set_data->wait, - set_data->ret != CMF_PENDING)) { + if (wait_event_interruptible(s.wait, s.ret != 1)) { spin_lock_irq(cdev->ccwlock); - if (set_data->ret == CMF_PENDING) { - set_data->ret = -ERESTARTSYS; + 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); } - spin_lock_irq(cdev->ccwlock); - cdev->private->cmb_wait = NULL; - ret = set_data->ret; -out_put: - kref_put(&set_data->kref, cmf_set_schib_release); -out: + return s.ret; + +out_nowait: spin_unlock_irq(cdev->ccwlock); - return ret; + return s.ret; } void retry_set_schib(struct ccw_device *cdev) { - struct set_schib_struct *set_data; - - set_data = cdev->private->cmb_wait; - if (!set_data) { - WARN_ON(1); - return; - } - kref_get(&set_data->kref); - set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, - set_data->address); - wake_up(&set_data->wait); - kref_put(&set_data->kref, cmf_set_schib_release); -} - -static int cmf_copy_block(struct ccw_device *cdev) -{ - struct subchannel *sch; - void *reference_buf; - void *hw_block; - struct cmb_data *cmb_data; - - sch = to_subchannel(cdev->dev.parent); - - if (stsch(sch->schid, &sch->schib)) - return -ENODEV; - - if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) { - /* Don't copy if a start function is in progress. */ - if ((!sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED) && - (sch->schib.scsw.actl & - (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && - (!sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) - return -EBUSY; - } - cmb_data = cdev->private->cmb; - hw_block = cmbops->align(cmb_data->hw_block); - if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size)) - /* No need to copy. */ - return 0; - reference_buf = kzalloc(cmb_data->size, GFP_ATOMIC); - if (!reference_buf) - return -ENOMEM; - /* Ensure consistency of block copied from hardware. */ - do { - memcpy(cmb_data->last_block, hw_block, cmb_data->size); - memcpy(reference_buf, hw_block, cmb_data->size); - } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size)); - cmb_data->last_update = get_clock(); - kfree(reference_buf); - return 0; -} - -struct copy_block_struct { - wait_queue_head_t wait; - int ret; - struct kref kref; -}; - -static void cmf_copy_block_release(struct kref *kref) -{ - struct copy_block_struct *copy_block; - - copy_block = container_of(kref, struct copy_block_struct, kref); - kfree(copy_block); -} - -static int cmf_cmb_copy_wait(struct ccw_device *cdev) -{ - struct copy_block_struct *copy_block; - int ret; - unsigned long flags; - - spin_lock_irqsave(cdev->ccwlock, flags); - if (!cdev->private->cmb) { - ret = -ENODEV; - goto out; - } - copy_block = kzalloc(sizeof(struct copy_block_struct), GFP_ATOMIC); - if (!copy_block) { - ret = -ENOMEM; - goto out; - } - init_waitqueue_head(©_block->wait); - kref_init(©_block->kref); - - ret = cmf_copy_block(cdev); - if (ret != -EBUSY) - goto out_put; - - if (cdev->private->state != DEV_STATE_ONLINE) { - ret = -EBUSY; - goto out_put; - } - - cdev->private->state = DEV_STATE_CMFUPDATE; - copy_block->ret = CMF_PENDING; - cdev->private->cmb_wait = copy_block; - - spin_unlock_irqrestore(cdev->ccwlock, flags); - if (wait_event_interruptible(copy_block->wait, - copy_block->ret != CMF_PENDING)) { - spin_lock_irqsave(cdev->ccwlock, flags); - if (copy_block->ret == CMF_PENDING) { - copy_block->ret = -ERESTARTSYS; - if (cdev->private->state == DEV_STATE_CMFUPDATE) - cdev->private->state = DEV_STATE_ONLINE; - } - spin_unlock_irqrestore(cdev->ccwlock, flags); - } - spin_lock_irqsave(cdev->ccwlock, flags); - cdev->private->cmb_wait = NULL; - ret = copy_block->ret; -out_put: - kref_put(©_block->kref, cmf_copy_block_release); -out: - spin_unlock_irqrestore(cdev->ccwlock, flags); - return ret; -} - -void cmf_retry_copy_block(struct ccw_device *cdev) -{ - struct copy_block_struct *copy_block; + struct set_schib_struct *s; - copy_block = cdev->private->cmb_wait; - if (!copy_block) { + s = cdev->private->cmb_wait; + cdev->private->cmb_wait = 0; + if (!s) { WARN_ON(1); return; } - kref_get(©_block->kref); - copy_block->ret = cmf_copy_block(cdev); - wake_up(©_block->wait); - kref_put(©_block->kref, cmf_copy_block_release); -} - -static void cmf_generic_reset(struct ccw_device *cdev) -{ - struct cmb_data *cmb_data; - - spin_lock_irq(cdev->ccwlock); - cmb_data = cdev->private->cmb; - if (cmb_data) { - memset(cmb_data->last_block, 0, cmb_data->size); - /* - * Need to reset hw block as well to make the hardware start - * from 0 again. - */ - 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); + s->ret = set_schib(cdev, s->mme, s->mbfc, s->address); + wake_up(&s->wait); } /** @@ -519,8 +343,8 @@ struct cmb { /* insert a single device into the cmb_area list * called with cmb_area.lock held from alloc_cmb */ -static inline int alloc_cmb_single (struct ccw_device *cdev, - struct cmb_data *cmb_data) +static inline int +alloc_cmb_single (struct ccw_device *cdev) { struct cmb *cmb; struct ccw_device_private *node; @@ -534,12 +358,10 @@ static inline int alloc_cmb_single (struct ccw_device *cdev, /* find first unused cmb in cmb_area.mem. * this is a little tricky: cmb_area.list - * remains sorted by ->cmb->hw_data pointers */ + * remains sorted by ->cmb 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) + if ((struct cmb*)node->cmb > cmb) break; cmb++; } @@ -550,8 +372,7 @@ static inline int alloc_cmb_single (struct ccw_device *cdev, /* insert new cmb */ list_add_tail(&cdev->private->cmb_list, &node->cmb_list); - cmb_data->hw_block = cmb; - cdev->private->cmb = cmb_data; + cdev->private->cmb = cmb; ret = 0; out: spin_unlock_irq(cdev->ccwlock); @@ -564,19 +385,7 @@ 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) { @@ -605,36 +414,29 @@ alloc_cmb (struct ccw_device *cdev) } /* do the actual allocation */ - ret = alloc_cmb_single(cdev, cmb_data); + ret = alloc_cmb_single(cdev); 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) +static void +free_cmb(struct ccw_device *cdev) { struct ccw_device_private *priv; - struct cmb_data *cmb_data; + + priv = cdev->private; 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)) { @@ -649,97 +451,83 @@ out: spin_unlock(&cmb_area.lock); } -static int set_cmb(struct ccw_device *cdev, u32 mme) +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); + if (!cdev->private->cmb) 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); + + offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0; return set_schib_wait(cdev, mme, 0, offset); } -static u64 read_cmb (struct ccw_device *cdev, int index) +static u64 +read_cmb (struct ccw_device *cdev, int index) { - struct cmb *cmb; - u32 val; - int ret; + /* 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; - - ret = cmf_cmb_copy_wait(cdev); - if (ret < 0) - return 0; + u32 val; spin_lock_irqsave(cdev->ccwlock, flags); if (!cdev->private->cmb) { - ret = 0; - goto out; + spin_unlock_irqrestore(cdev->ccwlock, flags); + return 0; } - cmb = ((struct cmb_data *)cdev->private->cmb)->last_block; + + cmb = *(struct cmb*)cdev->private->cmb; + spin_unlock_irqrestore(cdev->ccwlock, flags); switch (index) { case cmb_ssch_rsch_count: - ret = cmb->ssch_rsch_count; - goto out; + return cmb.ssch_rsch_count; case cmb_sample_count: - ret = cmb->sample_count; - goto out; + return cmb.sample_count; case cmb_device_connect_time: - val = cmb->device_connect_time; + val = cmb.device_connect_time; break; case cmb_function_pending_time: - val = cmb->function_pending_time; + val = cmb.function_pending_time; break; case cmb_device_disconnect_time: - val = cmb->device_disconnect_time; + val = cmb.device_disconnect_time; break; case cmb_control_unit_queuing_time: - val = cmb->control_unit_queuing_time; + val = cmb.control_unit_queuing_time; break; case cmb_device_active_only_time: - val = cmb->device_active_only_time; + val = cmb.device_active_only_time; break; default: - ret = 0; - goto out; + return 0; } - ret = time_to_avg_nsec(val, cmb->sample_count); -out: - spin_unlock_irqrestore(cdev->ccwlock, flags); - return ret; + return time_to_avg_nsec(val, cmb.sample_count); } -static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data) +static int +readall_cmb (struct ccw_device *cdev, struct cmbdata *data) { - struct cmb *cmb; - struct cmb_data *cmb_data; - u64 time; + /* 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; - int ret; + u64 time; - 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; + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return -ENODEV; } - cmb = cmb_data->last_block; - time = cmb_data->last_update - cdev->private->cmb_start_time; + + cmb = *(struct cmb*)cdev->private->cmb; + time = get_clock() - cdev->private->cmb_start_time; + spin_unlock_irqrestore(cdev->ccwlock, flags); memset(data, 0, sizeof(struct cmbdata)); @@ -750,32 +538,31 @@ static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data) 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; + 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->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); + = 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; -} + = time_to_nsec(cmb.device_active_only_time); -static void reset_cmb(struct ccw_device *cdev) -{ - cmf_generic_reset(cdev); + return 0; } -static void * align_cmb(void *area) +static void +reset_cmb(struct ccw_device *cdev) { - return area; + struct cmb *cmb; + spin_lock_irq(cdev->ccwlock); + cmb = cdev->private->cmb; + if (cmb) + memset (cmb, 0, sizeof (*cmb)); + cdev->private->cmb_start_time = get_clock(); + spin_unlock_irq(cdev->ccwlock); } static struct attribute_group cmf_attr_group; @@ -787,7 +574,6 @@ static struct cmb_operations cmbops_basic = { .read = read_cmb, .readall = readall_cmb, .reset = reset_cmb, - .align = align_cmb, .attr_group = &cmf_attr_group, }; @@ -824,34 +610,22 @@ static inline struct cmbe* cmbe_align(struct cmbe *c) return (struct cmbe*)addr; } -static int alloc_cmbe (struct ccw_device *cdev) +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); + cmbe = kmalloc (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) { + kfree(cmbe); spin_unlock_irq(cdev->ccwlock); - ret = -EBUSY; - goto out_free; + return -EBUSY; } - cmb_data->hw_block = cmbe; - cdev->private->cmb = cmb_data; + + cdev->private->cmb = cmbe; spin_unlock_irq(cdev->ccwlock); /* activate global measurement if this is the first channel */ @@ -862,24 +636,14 @@ static int alloc_cmbe (struct ccw_device *cdev) 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) +static void +free_cmbe (struct ccw_device *cdev) { - struct cmb_data *cmb_data; - spin_lock_irq(cdev->ccwlock); - cmb_data = cdev->private->cmb; + kfree(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 */ @@ -890,105 +654,89 @@ static void free_cmbe (struct ccw_device *cdev) spin_unlock(&cmb_area.lock); } -static int set_cmbe(struct ccw_device *cdev, u32 mme) +static int +set_cmbe(struct ccw_device *cdev, u32 mme) { unsigned long mba; - struct cmb_data *cmb_data; - unsigned long flags; - spin_lock_irqsave(cdev->ccwlock, flags); - if (!cdev->private->cmb) { - spin_unlock_irqrestore(cdev->ccwlock, flags); + if (!cdev->private->cmb) return -EINVAL; - } - cmb_data = cdev->private->cmb; - mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0; - spin_unlock_irqrestore(cdev->ccwlock, flags); + mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0; return set_schib_wait(cdev, mme, 1, mba); } -static u64 read_cmbe (struct ccw_device *cdev, int index) +u64 +read_cmbe (struct ccw_device *cdev, int index) { - struct cmbe *cmb; - struct cmb_data *cmb_data; - u32 val; - int ret; + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmbe cmb; unsigned long flags; - - ret = cmf_cmb_copy_wait(cdev); - if (ret < 0) - return 0; + u32 val; spin_lock_irqsave(cdev->ccwlock, flags); - cmb_data = cdev->private->cmb; - if (!cmb_data) { - ret = 0; - goto out; + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return 0; } - cmb = cmb_data->last_block; + + cmb = *cmbe_align(cdev->private->cmb); + spin_unlock_irqrestore(cdev->ccwlock, flags); switch (index) { case cmb_ssch_rsch_count: - ret = cmb->ssch_rsch_count; - goto out; + return cmb.ssch_rsch_count; case cmb_sample_count: - ret = cmb->sample_count; - goto out; + return cmb.sample_count; case cmb_device_connect_time: - val = cmb->device_connect_time; + val = cmb.device_connect_time; break; case cmb_function_pending_time: - val = cmb->function_pending_time; + val = cmb.function_pending_time; break; case cmb_device_disconnect_time: - val = cmb->device_disconnect_time; + val = cmb.device_disconnect_time; break; case cmb_control_unit_queuing_time: - val = cmb->control_unit_queuing_time; + val = cmb.control_unit_queuing_time; break; case cmb_device_active_only_time: - val = cmb->device_active_only_time; + val = cmb.device_active_only_time; break; case cmb_device_busy_time: - val = cmb->device_busy_time; + val = cmb.device_busy_time; break; case cmb_initial_command_response_time: - val = cmb->initial_command_response_time; + val = cmb.initial_command_response_time; break; default: - ret = 0; - goto out; + return 0; } - ret = time_to_avg_nsec(val, cmb->sample_count); -out: - spin_unlock_irqrestore(cdev->ccwlock, flags); - return ret; + return time_to_avg_nsec(val, cmb.sample_count); } -static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) +static int +readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) { - struct cmbe *cmb; - struct cmb_data *cmb_data; - u64 time; + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmbe cmb; unsigned long flags; - int ret; + u64 time; - 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; + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return -ENODEV; } - time = cmb_data->last_update - cdev->private->cmb_start_time; + + cmb = *cmbe_align(cdev->private->cmb); + time = get_clock() - cdev->private->cmb_start_time; + spin_unlock_irqrestore(cdev->ccwlock, flags); memset (data, 0, sizeof(struct cmbdata)); @@ -998,38 +746,35 @@ static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) /* conver to nanoseconds */ data->elapsed_time = (time * 1000) >> 12; - cmb = cmb_data->last_block; /* copy data to new structure */ - data->ssch_rsch_count = cmb->ssch_rsch_count; - data->sample_count = cmb->sample_count; + 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->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); + = time_to_nsec(cmb.control_unit_queuing_time); data->device_active_only_time - = time_to_nsec(cmb->device_active_only_time); - data->device_busy_time = time_to_nsec(cmb->device_busy_time); + = time_to_nsec(cmb.device_active_only_time); + data->device_busy_time = time_to_nsec(cmb.device_busy_time); data->initial_command_response_time - = time_to_nsec(cmb->initial_command_response_time); - - ret = 0; -out: - spin_unlock_irqrestore(cdev->ccwlock, flags); - return ret; -} + = time_to_nsec(cmb.initial_command_response_time); -static void reset_cmbe(struct ccw_device *cdev) -{ - cmf_generic_reset(cdev); + return 0; } -static void * align_cmbe(void *area) +static void +reset_cmbe(struct ccw_device *cdev) { - return cmbe_align(area); + struct cmbe *cmb; + spin_lock_irq(cdev->ccwlock); + cmb = cmbe_align(cdev->private->cmb); + if (cmb) + memset (cmb, 0, sizeof (*cmb)); + cdev->private->cmb_start_time = get_clock(); + spin_unlock_irq(cdev->ccwlock); } static struct attribute_group cmf_attr_group_ext; @@ -1041,7 +786,6 @@ static struct cmb_operations cmbops_extended = { .read = read_cmbe, .readall = readall_cmbe, .reset = reset_cmbe, - .align = align_cmbe, .attr_group = &cmf_attr_group_ext, }; @@ -1059,20 +803,14 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, struct ccw_device *cdev; long interval; unsigned long count; - struct cmb_data *cmb_data; cdev = to_ccwdev(dev); + interval = get_clock() - cdev->private->cmb_start_time; count = cmf_read(cdev, cmb_sample_count); - spin_lock_irq(cdev->ccwlock); - cmb_data = cdev->private->cmb; - if (count) { - interval = cmb_data->last_update - - cdev->private->cmb_start_time; - interval = (interval * 1000) >> 12; + if (count) interval /= count; - } else + else interval = -1; - spin_unlock_irq(cdev->ccwlock); return sprintf(buf, "%ld\n", interval); } @@ -1085,10 +823,7 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char int ret; ret = cmf_readall(to_ccwdev(dev), &data); - if (ret == -EAGAIN || ret == -ENODEV) - /* No data (yet/currently) available to use for calculation. */ - return sprintf(buf, "n/a\n"); - else if (ret) + if (ret) return ret; utilization = data.device_connect_time + @@ -1141,7 +876,7 @@ static struct attribute *cmf_attributes[] = { &dev_attr_avg_device_disconnect_time.attr, &dev_attr_avg_control_unit_queuing_time.attr, &dev_attr_avg_device_active_only_time.attr, - NULL, + 0, }; static struct attribute_group cmf_attr_group = { @@ -1161,7 +896,7 @@ static struct attribute *cmf_attributes_ext[] = { &dev_attr_avg_device_active_only_time.attr, &dev_attr_avg_device_busy_time.attr, &dev_attr_avg_initial_command_response_time.attr, - NULL, + 0, }; static struct attribute_group cmf_attr_group_ext = { @@ -1247,13 +982,6 @@ cmf_readall(struct ccw_device *cdev, struct cmbdata *data) return cmbops->readall(cdev, data); } -/* Reenable cmf when a disconnected device becomes available again. */ -int cmf_reenable(struct ccw_device *cdev) -{ - cmbops->reset(cdev); - return cmbops->set(cdev, 2); -} - static int __init init_cmf(void) {