Commit 4878dffc authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

[PATCH] fix OOPS in i2c sysctl registration

I had to rewrite the code from scratch to understand what it does,
but at least it doesn't OOPS anymore on boot..
parent 1524c2f2
...@@ -35,8 +35,6 @@ ...@@ -35,8 +35,6 @@
#include <linux/i2c-proc.h> #include <linux/i2c-proc.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
static int i2c_create_name(char **name, const char *prefix,
struct i2c_adapter *adapter, int addr);
static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, static int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
long *results, int magnitude); long *results, int magnitude);
static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize,
...@@ -54,15 +52,6 @@ static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; ...@@ -54,15 +52,6 @@ static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX];
static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX];
static ctl_table sysctl_table[] = {
{CTL_DEV, "dev", NULL, 0, 0555},
{0},
{DEV_SENSORS, "sensors", NULL, 0, 0555},
{0},
{0, NULL, NULL, 0, 0555},
{0}
};
static ctl_table i2c_proc_dev_sensors[] = { static ctl_table i2c_proc_dev_sensors[] = {
{SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips,
&i2c_sysctl_chips}, &i2c_sysctl_chips},
...@@ -87,36 +76,40 @@ static struct ctl_table_header *i2c_proc_header; ...@@ -87,36 +76,40 @@ static struct ctl_table_header *i2c_proc_header;
(for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
a LM75 chip on the third i2c bus at address 0x4e). a LM75 chip on the third i2c bus at address 0x4e).
name is allocated first. */ name is allocated first. */
static int i2c_create_name(char **name, const char *prefix, static char *generate_name(struct i2c_client *client, const char *prefix)
struct i2c_adapter *adapter, int addr)
{ {
char name_buffer[50]; struct i2c_adapter *adapter = client->adapter;
int id, i, end; int addr = client->addr;
if (i2c_is_isa_adapter(adapter)) char name_buffer[50], *name;
if (i2c_is_isa_adapter(adapter)) {
sprintf(name_buffer, "%s-isa-%04x", prefix, addr); sprintf(name_buffer, "%s-isa-%04x", prefix, addr);
else if (!adapter->algo->smbus_xfer && !adapter->algo->master_xfer) { } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) {
/* dummy adapter, generate prefix */ int id = i2c_adapter_id(adapter);
if (id < 0)
return ERR_PTR(-ENOENT);
sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
} else { /* dummy adapter, generate prefix */
int end, i;
sprintf(name_buffer, "%s-", prefix); sprintf(name_buffer, "%s-", prefix);
end = strlen(name_buffer); end = strlen(name_buffer);
for(i = 0; i < 32; i++) {
if(adapter->algo->name[i] == ' ') for (i = 0; i < 32; i++) {
if (adapter->algo->name[i] == ' ')
break; break;
name_buffer[end++] = tolower(adapter->algo->name[i]); name_buffer[end++] = tolower(adapter->algo->name[i]);
} }
name_buffer[end] = 0; name_buffer[end] = 0;
sprintf(name_buffer + end, "-%04x", addr); sprintf(name_buffer + end, "-%04x", addr);
} else {
if ((id = i2c_adapter_id(adapter)) < 0)
return -ENOENT;
sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
}
*name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
if (!*name) {
printk (KERN_WARNING "i2c_create_name: not enough memory\n");
return -ENOMEM;
} }
strcpy(*name, name_buffer);
return 0; name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
if (unlikely(!name))
return ERR_PTR(-ENOMEM);
strcpy(name, name_buffer);
return name;
} }
/* This rather complex function must be called when you want to add an entry /* This rather complex function must be called when you want to add an entry
...@@ -127,93 +120,80 @@ static int i2c_create_name(char **name, const char *prefix, ...@@ -127,93 +120,80 @@ static int i2c_create_name(char **name, const char *prefix,
If any driver wants subdirectories within the newly created directory, If any driver wants subdirectories within the newly created directory,
this function must be updated! */ this function must be updated! */
int i2c_register_entry(struct i2c_client *client, const char *prefix, int i2c_register_entry(struct i2c_client *client, const char *prefix,
ctl_table * ctl_template) struct ctl_table *leaf)
{ {
int i, res, len, id; struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl;
ctl_table *new_table, *client_tbl, *tbl; struct ctl_table_header *hdr;
char *name; struct ctl_table *tmp;
struct ctl_table_header *new_header; const char *name;
int id;
name = generate_name(client, prefix);
if (IS_ERR(name))
return PTR_ERR(name);
for (id = 0; id < SENSORS_ENTRY_MAX; id++) {
if (!i2c_entries[id])
goto free_slot;
}
if ((res = i2c_create_name(&name, prefix, client->adapter, goto out_free_name;
client->addr))) return res;
for (id = 0; id < SENSORS_ENTRY_MAX; id++) free_slot:
if (!i2c_entries[id]) { tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
break; if (unlikely(!tbl))
} goto out_free_name;
if (id == SENSORS_ENTRY_MAX) { memset(tbl, 0, sizeof(*tbl));
kfree(name);
return -ENOMEM;
}
id += 256; for (tmp = leaf; tmp->ctl_name; tmp++)
tmp->extra2 = client;
len = 0;
while (ctl_template[len].procname)
len++;
if (!(new_table = kmalloc(sizeof(sysctl_table) + sizeof(ctl_table) * (len + 1),
GFP_KERNEL))) {
kfree(name);
return -ENOMEM;
}
memcpy(new_table, sysctl_table, sizeof(sysctl_table)); tbl->sensors->ctl_name = id+256;
tbl = new_table; /* sys/ */ tbl->sensors->procname = name;
tbl = tbl->child = tbl + 2; /* dev/ */ tbl->sensors->mode = 0555;
tbl = tbl->child = tbl + 2; /* sensors/ */ tbl->sensors->child = leaf;
client_tbl = tbl->child = tbl + 2; /* XX-chip-YY-ZZ/ */
client_tbl->procname = name; tbl->dev->ctl_name = DEV_SENSORS;
client_tbl->ctl_name = id; tbl->dev->procname = "sensors";
client_tbl->child = client_tbl + 2; tbl->dev->mode = 0555;
tbl->dev->child = tbl->sensors;
/* Next the client sysctls. --km */ tbl->root->ctl_name = CTL_DEV;
tbl = client_tbl->child; tbl->root->procname = "dev";
memcpy(tbl, ctl_template, sizeof(ctl_table) * (len+1)); tbl->root->mode = 0555;
for (i = 0; i < len; i++) tbl->root->child = tbl->dev;
tbl[i].extra2 = client;
if (!(new_header = register_sysctl_table(new_table, 0))) { hdr = register_sysctl_table(tbl->root, 0);
printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); if (unlikely(!hdr))
kfree(new_table); goto out_free_tbl;
kfree(name);
return -EPERM;
}
i2c_entries[id - 256] = new_header; i2c_entries[id] = hdr;
i2c_clients[id] = client;
i2c_clients[id - 256] = client; return (id + 256); /* XXX(hch) why?? */
#ifdef DEBUG out_free_tbl:
if (!new_header || !new_header->ctl_table || kfree(tbl);
!new_header->ctl_table->child || out_free_name:
!new_header->ctl_table->child->child || kfree(name);
!new_header->ctl_table->child->child->de ) { return -ENOMEM;
printk
(KERN_ERR "i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n");
return id;
}
#endif /* DEBUG */
client_tbl->de->owner = client->driver->owner;
return id;
} }
void i2c_deregister_entry(int id) void i2c_deregister_entry(int id)
{ {
ctl_table *table; id -= 256;
char *temp;
id -= 256;
if (i2c_entries[id]) { if (i2c_entries[id]) {
table = i2c_entries[id]->ctl_table; struct ctl_table_header *hdr = i2c_entries[id];
unregister_sysctl_table(i2c_entries[id]); struct ctl_table *tbl = hdr->ctl_table;
/* 2-step kfree needed to keep gcc happy about const points */
(const char *) temp = table[4].procname; unregister_sysctl_table(hdr);
kfree(temp); kfree(tbl->child->child->procname);
kfree(table); kfree(tbl); /* actually the whole anonymous struct */
i2c_entries[id] = NULL;
i2c_clients[id] = NULL;
} }
i2c_entries[id] = NULL;
i2c_clients[id] = NULL;
} }
static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment