]> git.hungrycats.org Git - linux/commitdiff
[PATCH] fix OOPS in i2c sysctl registration
authorChristoph Hellwig <hch@lst.de>
Fri, 14 Mar 2003 11:35:27 +0000 (03:35 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Fri, 14 Mar 2003 11:35:27 +0000 (03:35 -0800)
I had to rewrite the code from scratch to understand what it does,
but at least it doesn't OOPS anymore on boot..

drivers/i2c/i2c-proc.c

index 2b2e752e07696a2f9cd2e7c814c533057f74d425..73bb33815907d4073d4b0f63cceb2d79def107eb 100644 (file)
@@ -35,8 +35,6 @@
 #include <linux/i2c-proc.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,
                               long *results, int magnitude);
 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];
 
 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[] = {
        {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips,
         &i2c_sysctl_chips},
@@ -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
    a LM75 chip on the third i2c bus at address 0x4e).  
    name is allocated first. */
-static int i2c_create_name(char **name, const char *prefix,
-                       struct i2c_adapter *adapter, int addr)
+static char *generate_name(struct i2c_client *client, const char *prefix)
 {
-       char name_buffer[50];
-       int id, i, end;
-       if (i2c_is_isa_adapter(adapter))
+       struct i2c_adapter *adapter = client->adapter;
+       int addr = client->addr;
+       char name_buffer[50], *name;
+
+       if (i2c_is_isa_adapter(adapter)) {
                sprintf(name_buffer, "%s-isa-%04x", prefix, addr);
-       else if (!adapter->algo->smbus_xfer && !adapter->algo->master_xfer) {
-               /* dummy adapter, generate prefix */
+       } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) {
+               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);
                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;
                        name_buffer[end++] = tolower(adapter->algo->name[i]);
                }
+
                name_buffer[end] = 0;
                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
@@ -127,93 +120,80 @@ static int i2c_create_name(char **name, const char *prefix,
    If any driver wants subdirectories within the newly created directory,
    this function must be updated!  */
 int i2c_register_entry(struct i2c_client *client, const char *prefix,
-                          ctl_table * ctl_template)
+                      struct ctl_table *leaf)
 {
-       int i, res, len, id;
-       ctl_table *new_table, *client_tbl, *tbl;
-       char *name;
-       struct ctl_table_header *new_header;
+       struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl;
+       struct ctl_table_header *hdr;
+       struct ctl_table *tmp;
+       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,
-                                      client->addr))) return res;
+       goto out_free_name;
 
-       for (id = 0; id < SENSORS_ENTRY_MAX; id++)
-               if (!i2c_entries[id]) {
-                       break;
-               }
-       if (id == SENSORS_ENTRY_MAX) {
-               kfree(name);
-               return -ENOMEM;
-       }
+ free_slot:
+       tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+       if (unlikely(!tbl))
+               goto out_free_name;
+       memset(tbl, 0, sizeof(*tbl));
 
-       id += 256;
-       
-       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;
-       }
+       for (tmp = leaf; tmp->ctl_name; tmp++)
+               tmp->extra2 = client;
 
-       memcpy(new_table, sysctl_table, sizeof(sysctl_table));
-       tbl = new_table; /* sys/ */
-       tbl = tbl->child = tbl + 2; /* dev/ */
-       tbl = tbl->child = tbl + 2; /* sensors/ */      
-               client_tbl = tbl->child = tbl + 2; /* XX-chip-YY-ZZ/ */
+       tbl->sensors->ctl_name = id+256;
+       tbl->sensors->procname = name;
+       tbl->sensors->mode = 0555;
+       tbl->sensors->child = leaf;
 
-       client_tbl->procname = name;
-       client_tbl->ctl_name = id;
-       client_tbl->child = client_tbl + 2;
+       tbl->dev->ctl_name = DEV_SENSORS;
+       tbl->dev->procname = "sensors";
+       tbl->dev->mode = 0555;
+       tbl->dev->child = tbl->sensors;
 
-       /* Next the client sysctls. --km */
-       tbl = client_tbl->child;
-       memcpy(tbl, ctl_template, sizeof(ctl_table) * (len+1));
-       for (i = 0; i < len; i++)
-               tbl[i].extra2 = client;
+       tbl->root->ctl_name = CTL_DEV;
+       tbl->root->procname = "dev";
+       tbl->root->mode = 0555;
+       tbl->root->child = tbl->dev;
 
-       if (!(new_header = register_sysctl_table(new_table, 0))) {
-               printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n");
-               kfree(new_table);
-               kfree(name);
-               return -EPERM;
-       }
+       hdr = register_sysctl_table(tbl->root, 0);
+       if (unlikely(!hdr))
+               goto out_free_tbl;
 
-       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
-       if (!new_header || !new_header->ctl_table ||
-           !new_header->ctl_table->child ||
-           !new_header->ctl_table->child->child ||
-           !new_header->ctl_table->child->child->de ) {
-               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;
+ out_free_tbl:
+       kfree(tbl);
+ out_free_name:
+       kfree(name);
+       return -ENOMEM;
 }
 
 void i2c_deregister_entry(int id)
 {
-       ctl_table *table;
-       char *temp;
+       id -= 256;
 
-       id -= 256;      
        if (i2c_entries[id]) {
-               table = i2c_entries[id]->ctl_table;
-               unregister_sysctl_table(i2c_entries[id]);
-               /* 2-step kfree needed to keep gcc happy about const points */
-               (const char *) temp = table[4].procname;
-               kfree(temp);
-               kfree(table);
-               i2c_entries[id] = NULL;
-               i2c_clients[id] = NULL;
+               struct ctl_table_header *hdr = i2c_entries[id];
+               struct ctl_table *tbl = hdr->ctl_table;
+
+               unregister_sysctl_table(hdr);
+               kfree(tbl->child->child->procname);
+               kfree(tbl); /* actually the whole anonymous struct */
        }
+
+       i2c_entries[id] = NULL;
+       i2c_clients[id] = NULL;
 }
 
 static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp,