linux-stable/drivers/w1/slaves/w1_bq27000.c
NeilBrown b02f8bede2 W1: split master mutex to avoid deadlocks.
The 'mutex' in struct w1_master is use for two very different
purposes.

Firstly it protects various data structures such as the list of all
slaves.

Secondly it protects the w1 buss against concurrent accesses.

This can lead to deadlocks when the ->probe code called while adding a
slave needs to talk on the bus, as is the case for power_supply
devices.
ds2780 and ds2781 drivers contain a work around to track which
process hold the lock simply to avoid this deadlock.  bq27000 doesn't
have that work around and so deadlocks.

There are other possible deadlocks involving sysfs.
When removing a device the sysfs s_active lock is held, so the lock
that protects the slave list must take precedence over s_active.
However when access power_supply attributes via sysfs, the s_active
lock must take precedence over the lock that protects accesses to
the bus.

So to avoid deadlocks between w1 slaves and sysfs, these must be
two separate locks.  Making them separate means that the work around
in ds2780 and ds2781 can be removed.

So this patch:
 - adds a new mutex: "bus_mutex" which serialises access to the bus.
 - takes in mutex in w1_search and ds1wm_search while they access
   the bus for searching.  The mutex is dropped before calling the
   callback which adds the slave.
 - changes all slaves to use bus_mutex instead of mutex to
   protect access to the bus
 - removes w1_ds2790_io_nolock and w1_ds2781_io_nolock, and the
   related code from drivers/power/ds278[01]_battery.c which
   calls them.

Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-06-13 16:38:40 -07:00

115 lines
2.4 KiB
C

/*
* drivers/w1/slaves/w1_bq27000.c
*
* Copyright (C) 2007 Texas Instruments, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/power/bq27x00_battery.h>
#include "../w1.h"
#include "../w1_int.h"
#include "../w1_family.h"
#define HDQ_CMD_READ (0)
#define HDQ_CMD_WRITE (1<<7)
static int F_ID;
static int w1_bq27000_read(struct device *dev, unsigned int reg)
{
u8 val;
struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev);
mutex_lock(&sl->master->bus_mutex);
w1_write_8(sl->master, HDQ_CMD_READ | reg);
val = w1_read_8(sl->master);
mutex_unlock(&sl->master->bus_mutex);
return val;
}
static struct bq27000_platform_data bq27000_battery_info = {
.read = w1_bq27000_read,
.name = "bq27000-battery",
};
static int w1_bq27000_add_slave(struct w1_slave *sl)
{
int ret;
struct platform_device *pdev;
pdev = platform_device_alloc("bq27000-battery", -1);
if (!pdev) {
ret = -ENOMEM;
return ret;
}
ret = platform_device_add_data(pdev,
&bq27000_battery_info,
sizeof(bq27000_battery_info));
pdev->dev.parent = &sl->dev;
ret = platform_device_add(pdev);
if (ret)
goto pdev_add_failed;
dev_set_drvdata(&sl->dev, pdev);
goto success;
pdev_add_failed:
platform_device_unregister(pdev);
success:
return ret;
}
static void w1_bq27000_remove_slave(struct w1_slave *sl)
{
struct platform_device *pdev = dev_get_drvdata(&sl->dev);
platform_device_unregister(pdev);
}
static struct w1_family_ops w1_bq27000_fops = {
.add_slave = w1_bq27000_add_slave,
.remove_slave = w1_bq27000_remove_slave,
};
static struct w1_family w1_bq27000_family = {
.fid = 1,
.fops = &w1_bq27000_fops,
};
static int __init w1_bq27000_init(void)
{
if (F_ID)
w1_bq27000_family.fid = F_ID;
return w1_register_family(&w1_bq27000_family);
}
static void __exit w1_bq27000_exit(void)
{
w1_unregister_family(&w1_bq27000_family);
}
module_init(w1_bq27000_init);
module_exit(w1_bq27000_exit);
module_param(F_ID, int, S_IRUSR);
MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Texas Instruments Ltd");
MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip");