@@ -1139,7 +1139,11 @@ EXPORT_SYMBOL_GPL(i2c_new_ancillary_device);
static void i2c_adapter_dev_release(struct device *dev)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
- complete(&adap->dev_released);
+
+ /* free bus id */
+ mutex_lock(&core_lock);
+ idr_remove(&i2c_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
}
unsigned int i2c_adapter_depth(struct i2c_adapter *adapter)
@@ -1512,9 +1516,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
return 0;
out_reg:
- init_completion(&adap->dev_released);
device_unregister(&adap->dev);
- wait_for_completion(&adap->dev_released);
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
@@ -1713,25 +1715,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)
i2c_host_notify_irq_teardown(adap);
- /* wait until all references to the device are gone
- *
- * FIXME: This is old code and should ideally be replaced by an
- * alternative which results in decoupling the lifetime of the struct
- * device from the i2c_adapter, like spi or netdev do. Any solution
- * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled!
- */
- init_completion(&adap->dev_released);
device_unregister(&adap->dev);
- wait_for_completion(&adap->dev_released);
-
- /* free bus id */
- mutex_lock(&core_lock);
- idr_remove(&i2c_adapter_idr, adap->nr);
- mutex_unlock(&core_lock);
-
- /* Clear the device structure in case this adapter is ever going to be
- added again */
- memset(&adap->dev, 0, sizeof(adap->dev));
}
EXPORT_SYMBOL(i2c_del_adapter);
@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>
+#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -44,8 +45,14 @@ struct i2c_dev {
struct i2c_adapter *adap;
struct device dev;
struct cdev cdev;
+ struct rw_semaphore sem;
};
+static inline struct i2c_dev *to_i2c_dev(struct inode *ino)
+{
+ return container_of(ino->i_cdev, struct i2c_dev, cdev);
+}
+
#define I2C_MINORS (MINORMASK + 1)
static LIST_HEAD(i2c_dev_list);
static DEFINE_SPINLOCK(i2c_dev_list_lock);
@@ -136,15 +143,23 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
{
char *tmp;
int ret;
-
+ struct i2c_dev *i2c_dev = to_i2c_dev(file_inode(file));
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
+ down_read(&i2c_dev->sem);
+ if (!i2c_dev->adap) {
+ up_read(&i2c_dev->sem);
+ return -ENODEV;
+ }
+
tmp = kzalloc(count, GFP_KERNEL);
- if (tmp == NULL)
+ if (tmp == NULL) {
+ up_read(&i2c_dev->sem);
return -ENOMEM;
+ }
pr_debug("i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count);
@@ -152,6 +167,7 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
if (ret >= 0)
if (copy_to_user(buf, tmp, ret))
ret = -EFAULT;
+ up_read(&i2c_dev->sem);
kfree(tmp);
return ret;
}
@@ -161,18 +177,28 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
{
int ret;
char *tmp;
+ struct i2c_dev *i2c_dev = to_i2c_dev(file_inode(file));
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
+ down_read(&i2c_dev->sem);
+ if (!i2c_dev->adap) {
+ up_read(&i2c_dev->sem);
+ return -ENODEV;
+ }
+
tmp = memdup_user(buf, count);
- if (IS_ERR(tmp))
+ if (IS_ERR(tmp)) {
+ up_read(&i2c_dev->sem);
return PTR_ERR(tmp);
+ }
pr_debug("i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count);
ret = i2c_master_send(client, tmp, count);
+ up_read(&i2c_dev->sem);
kfree(tmp);
return ret;
}
@@ -389,7 +415,8 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
return res;
}
-static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long i2cdev_ioctl_unlocked(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
@@ -495,6 +522,20 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return 0;
}
+static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_dev *i2c_dev = to_i2c_dev(file_inode(file));
+ long ret;
+
+ down_read(&i2c_dev->sem);
+ if (!i2c_dev->adap)
+ ret = -ENODEV;
+ else
+ ret = i2cdev_ioctl_unlocked(file, cmd, arg);
+ up_read(&i2c_dev->sem);
+
+ return ret;
+}
#ifdef CONFIG_COMPAT
struct i2c_smbus_ioctl_data32 {
@@ -516,10 +557,12 @@ struct i2c_rdwr_ioctl_data32 {
u32 nmsgs;
};
-static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long compat_i2cdev_ioctl_unlocked(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
+
switch (cmd) {
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
@@ -578,19 +621,39 @@ static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned lo
return i2cdev_ioctl(file, cmd, arg);
}
}
+
+static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct i2c_dev *i2c_dev = to_i2c_dev(file_inode(file));
+ long ret;
+
+ down_read(&i2c_dev->sem);
+ if (!i2c_dev->adap)
+ ret = -ENODEV;
+ else
+ ret = compat_i2cdev_ioctl_unlocked(file, cmd, arg);
+ up_read(&i2c_dev->sem);
+
+ return ret;
+}
#else
#define compat_i2cdev_ioctl NULL
#endif
static int i2cdev_open(struct inode *inode, struct file *file)
{
- unsigned int minor = iminor(inode);
+ struct i2c_dev *i2c_dev = to_i2c_dev(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
+ int ret = 0;
- adap = i2c_get_adapter(minor);
- if (!adap)
- return -ENODEV;
+ down_read(&i2c_dev->sem);
+ adap = i2c_dev->adap;
+ if (!adap) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
@@ -601,22 +664,23 @@ static int i2cdev_open(struct inode *inode, struct file *file)
*/
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
- i2c_put_adapter(adap);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_unlock;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->adapter = adap;
file->private_data = client;
- return 0;
+out_unlock:
+ up_read(&i2c_dev->sem);
+ return ret;
}
static int i2cdev_release(struct inode *inode, struct file *file)
{
struct i2c_client *client = file->private_data;
- i2c_put_adapter(client->adapter);
kfree(client);
file->private_data = NULL;
@@ -669,6 +733,8 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy)
i2c_dev->dev.parent = &adap->dev;
i2c_dev->dev.release = i2cdev_dev_release;
+ init_rwsem(&i2c_dev->sem);
+
res = dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr);
if (res)
goto err_put_i2c_dev;
@@ -698,6 +764,10 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy)
if (!i2c_dev) /* attach_adapter must have failed */
return NOTIFY_DONE;
+ down_write(&i2c_dev->sem);
+ i2c_dev->adap = NULL;
+ up_write(&i2c_dev->sem);
+
put_i2c_dev(i2c_dev, true);
pr_debug("adapter [%s] unregistered\n", adap->name);
@@ -14,7 +14,6 @@
#include <linux/bits.h>
#include <linux/mod_devicetable.h>
#include <linux/device.h> /* for struct device */
-#include <linux/sched.h> /* for completion */
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
#include <linux/rtmutex.h>
@@ -739,7 +738,6 @@ struct i2c_adapter {
int nr;
char name[48];
- struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;