Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 64198
b: refs/heads/master
c: 8127a1f
h: refs/heads/master
v: v3
  • Loading branch information
Michael Holzheu authored and Martin Schwidefsky committed Aug 22, 2007
1 parent a42a61b commit dffb409
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 59 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0a87c5cfc0bb0c1bdcc1cc9fd82e4a1711fac512
refs/heads/master: 8127a1f80a002d02a30909ddf6187faedf89e00a
218 changes: 160 additions & 58 deletions trunk/drivers/s390/char/vmur.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,26 @@ static struct ccw_driver ur_driver = {
.set_offline = ur_set_offline,
};

static DEFINE_MUTEX(vmur_mutex);

/*
* Allocation, freeing, getting and putting of urdev structures
*
* Each ur device (urd) contains a reference to its corresponding ccw device
* (cdev) using the urd->cdev pointer. Each ccw device has a reference to the
* ur device using the cdev->dev.driver_data pointer.
*
* urd references:
* - ur_probe gets a urd reference, ur_remove drops the reference
* (cdev->dev.driver_data)
* - ur_open gets a urd reference, ur_relase drops the reference
* (urf->urd)
*
* cdev references:
* - urdev_alloc get a cdev reference (urd->cdev)
* - urdev_free drops the cdev reference (urd->cdev)
*
* Setting and clearing of cdev->dev.driver_data is protected by the ccwdev lock
*/
static struct urdev *urdev_alloc(struct ccw_device *cdev)
{
Expand All @@ -79,42 +97,61 @@ static struct urdev *urdev_alloc(struct ccw_device *cdev)
urd = kzalloc(sizeof(struct urdev), GFP_KERNEL);
if (!urd)
return NULL;
urd->cdev = cdev;
urd->reclen = cdev->id.driver_info;
ccw_device_get_id(cdev, &urd->dev_id);
mutex_init(&urd->io_mutex);
mutex_init(&urd->open_mutex);
atomic_set(&urd->ref_count, 1);
urd->cdev = cdev;
get_device(&cdev->dev);
return urd;
}

static void urdev_free(struct urdev *urd)
{
TRACE("urdev_free: %p\n", urd);
if (urd->cdev)
put_device(&urd->cdev->dev);
kfree(urd);
}

/*
* This is how the character device driver gets a reference to a
* ur device. When this call returns successfully, a reference has
* been taken (by get_device) on the underlying kobject. The recipient
* of this urdev pointer must eventually drop it with urdev_put(urd)
* which does the corresponding put_device().
*/
static void urdev_get(struct urdev *urd)
{
atomic_inc(&urd->ref_count);
}

static struct urdev *urdev_get_from_cdev(struct ccw_device *cdev)
{
struct urdev *urd;
unsigned long flags;

spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
urd = cdev->dev.driver_data;
if (urd)
urdev_get(urd);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return urd;
}

static struct urdev *urdev_get_from_devno(u16 devno)
{
char bus_id[16];
struct ccw_device *cdev;
struct urdev *urd;

sprintf(bus_id, "0.0.%04x", devno);
cdev = get_ccwdev_by_busid(&ur_driver, bus_id);
if (!cdev)
return NULL;

return cdev->dev.driver_data;
urd = urdev_get_from_cdev(cdev);
put_device(&cdev->dev);
return urd;
}

static void urdev_put(struct urdev *urd)
{
put_device(&urd->cdev->dev);
if (atomic_dec_and_test(&urd->ref_count))
urdev_free(urd);
}

/*
Expand Down Expand Up @@ -246,6 +283,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
urd = cdev->dev.driver_data;
BUG_ON(!urd);
/* On special conditions irb is an error pointer */
if (IS_ERR(irb))
urd->io_request_rc = PTR_ERR(irb);
Expand All @@ -263,9 +301,15 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
static ssize_t ur_attr_reclen_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct urdev *urd = dev->driver_data;
struct urdev *urd;
int rc;

return sprintf(buf, "%zu\n", urd->reclen);
urd = urdev_get_from_cdev(to_ccwdev(dev));
if (!urd)
return -ENODEV;
rc = sprintf(buf, "%zu\n", urd->reclen);
urdev_put(urd);
return rc;
}

static DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL);
Expand Down Expand Up @@ -726,64 +770,63 @@ static struct file_operations ur_fops = {

/*
* ccw_device infrastructure:
* ur_probe gets its own ref to the device (i.e. get_device),
* creates the struct urdev, the device attributes, sets up
* the interrupt handler and validates the virtual unit record device.
* ur_remove removes the device attributes, frees the struct urdev
* and drops (put_device) the ref to the device we got in ur_probe.
* ur_probe creates the struct urdev (with refcount = 1), the device
* attributes, sets up the interrupt handler and validates the virtual
* unit record device.
* ur_remove removes the device attributes and drops the reference to
* struct urdev.
*
* ur_probe, ur_remove, ur_set_online and ur_set_offline are serialized
* by the vmur_mutex lock.
*
* urd->char_device is used as indication that the online function has
* been completed successfully.
*/
static int ur_probe(struct ccw_device *cdev)
{
struct urdev *urd;
int rc;

TRACE("ur_probe: cdev=%p state=%d\n", cdev, *(int *) cdev->private);

if (!get_device(&cdev->dev))
return -ENODEV;
TRACE("ur_probe: cdev=%p\n", cdev);

mutex_lock(&vmur_mutex);
urd = urdev_alloc(cdev);
if (!urd) {
rc = -ENOMEM;
goto fail;
goto fail_unlock;
}

rc = ur_create_attributes(&cdev->dev);
if (rc) {
rc = -ENOMEM;
goto fail;
goto fail_urdev_put;
}
cdev->dev.driver_data = urd;
cdev->handler = ur_int_handler;

/* validate virtual unit record device */
urd->class = get_urd_class(urd);
if (urd->class < 0) {
rc = urd->class;
goto fail;
goto fail_remove_attr;
}
if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) {
rc = -ENOTSUPP;
goto fail;
goto fail_remove_attr;
}
spin_lock_irq(get_ccwdev_lock(cdev));
cdev->dev.driver_data = urd;
spin_unlock_irq(get_ccwdev_lock(cdev));

mutex_unlock(&vmur_mutex);
return 0;

fail:
urdev_free(urd);
put_device(&cdev->dev);
return rc;
}

static void ur_remove(struct ccw_device *cdev)
{
struct urdev *urd = cdev->dev.driver_data;

TRACE("ur_remove\n");
if (cdev->online)
ur_set_offline(cdev);
fail_remove_attr:
ur_remove_attributes(&cdev->dev);
urdev_free(urd);
put_device(&cdev->dev);
fail_urdev_put:
urdev_put(urd);
fail_unlock:
mutex_unlock(&vmur_mutex);
return rc;
}

static int ur_set_online(struct ccw_device *cdev)
Expand All @@ -792,20 +835,29 @@ static int ur_set_online(struct ccw_device *cdev)
int minor, major, rc;
char node_id[16];

TRACE("ur_set_online: cdev=%p state=%d\n", cdev,
*(int *) cdev->private);
TRACE("ur_set_online: cdev=%p\n", cdev);

if (!try_module_get(ur_driver.owner))
return -EINVAL;
mutex_lock(&vmur_mutex);
urd = urdev_get_from_cdev(cdev);
if (!urd) {
/* ur_remove already deleted our urd */
rc = -ENODEV;
goto fail_unlock;
}

if (urd->char_device) {
/* Another ur_set_online was faster */
rc = -EBUSY;
goto fail_urdev_put;
}

urd = (struct urdev *) cdev->dev.driver_data;
minor = urd->dev_id.devno;
major = MAJOR(ur_first_dev_maj_min);

urd->char_device = cdev_alloc();
if (!urd->char_device) {
rc = -ENOMEM;
goto fail_module_put;
goto fail_urdev_put;
}

cdev_init(urd->char_device, &ur_fops);
Expand Down Expand Up @@ -834,29 +886,79 @@ static int ur_set_online(struct ccw_device *cdev)
TRACE("ur_set_online: device_create rc=%d\n", rc);
goto fail_free_cdev;
}

urdev_put(urd);
mutex_unlock(&vmur_mutex);
return 0;

fail_free_cdev:
cdev_del(urd->char_device);
fail_module_put:
module_put(ur_driver.owner);

urd->char_device = NULL;
fail_urdev_put:
urdev_put(urd);
fail_unlock:
mutex_unlock(&vmur_mutex);
return rc;
}

static int ur_set_offline(struct ccw_device *cdev)
static int ur_set_offline_force(struct ccw_device *cdev, int force)
{
struct urdev *urd;
int rc;

TRACE("ur_set_offline: cdev=%p cdev->private=%p state=%d\n",
cdev, cdev->private, *(int *) cdev->private);
urd = (struct urdev *) cdev->dev.driver_data;
TRACE("ur_set_offline: cdev=%p\n", cdev);
urd = urdev_get_from_cdev(cdev);
if (!urd)
/* ur_remove already deleted our urd */
return -ENODEV;
if (!urd->char_device) {
/* Another ur_set_offline was faster */
rc = -EBUSY;
goto fail_urdev_put;
}
if (!force && (atomic_read(&urd->ref_count) > 2)) {
/* There is still a user of urd (e.g. ur_open) */
TRACE("ur_set_offline: BUSY\n");
rc = -EBUSY;
goto fail_urdev_put;
}
device_destroy(vmur_class, urd->char_device->dev);
cdev_del(urd->char_device);
module_put(ur_driver.owner);
urd->char_device = NULL;
rc = 0;

return 0;
fail_urdev_put:
urdev_put(urd);
return rc;
}

static int ur_set_offline(struct ccw_device *cdev)
{
int rc;

mutex_lock(&vmur_mutex);
rc = ur_set_offline_force(cdev, 0);
mutex_unlock(&vmur_mutex);
return rc;
}

static void ur_remove(struct ccw_device *cdev)
{
unsigned long flags;

TRACE("ur_remove\n");

mutex_lock(&vmur_mutex);

if (cdev->online)
ur_set_offline_force(cdev, 1);
ur_remove_attributes(&cdev->dev);

spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
urdev_put(cdev->dev.driver_data);
cdev->dev.driver_data = NULL;
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);

mutex_unlock(&vmur_mutex);
}

/*
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/s390/char/vmur.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct urdev {
size_t reclen; /* Record length for *write* CCWs */
int class; /* VM device class */
int io_request_rc; /* return code from I/O request */
atomic_t ref_count; /* reference counter */
};

/*
Expand Down

0 comments on commit dffb409

Please sign in to comment.