]> granicus.if.org Git - zfs/commitdiff
Linux 3.10 compat: Do not rely on struct proc_dir_entry definition
authorRichard Yao <ryao@gentoo.org>
Wed, 3 Jul 2013 20:34:52 +0000 (16:34 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 8 Jul 2013 22:25:18 +0000 (15:25 -0700)
Linux kernel commit torvalds/linux#59d8053f moved the definition of
struct proc_dir_entry from include/linux/proc_fs.h to the private
header fs/proc/internal.h. The SPL relied on that to map Solaris'
kstat to entries in /proc/spl/kstat.

Since the proc_dir_entry structure is now private the only safe
thing to do is wrap the opaque proc handle with our own structure.
This actually ends up simplify the code and is good because it
moves us away from depending on implementation details of /proc.

Signed-off-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #257

include/linux/proc_compat.h
include/sys/kstat.h
module/spl/spl-kstat.c
module/spl/spl-proc.c

index 434ffa3f1a2fc66f377848e191f577bf02638e74..7b044e7e1be6a4d40a297080525c0263820fb026 100644 (file)
@@ -43,9 +43,6 @@
 #endif
 
 extern struct proc_dir_entry *proc_spl_kstat;
-struct proc_dir_entry *proc_dir_entry_find(struct proc_dir_entry *root,
-                                          const char *str);
-int proc_dir_entries(struct proc_dir_entry *root);
 
 int spl_proc_init(void);
 void spl_proc_fini(void);
index 9275c1ea454c3649dc651912b2e26401b60351d1..da3c5899db0fedac26df5fddea06fc7fd0cf9718 100644 (file)
@@ -83,6 +83,13 @@ struct kstat_s;
 typedef int kid_t;                                  /* unique kstat id */
 typedef int kstat_update_t(struct kstat_s *, int);  /* dynamic update cb */
 
+typedef struct kstat_module {
+       char             ksm_name[KSTAT_STRLEN+1];  /* module name */
+       struct list_head ksm_module_list;           /* module linkage */
+       struct list_head ksm_kstat_list;            /* list of kstat entries */
+       struct proc_dir_entry *ksm_proc;            /* proc entry */
+} kstat_module_t;
+
 typedef struct kstat_s {
        int              ks_magic;                  /* magic value */
         kid_t            ks_kid;                    /* unique kstat ID */
@@ -102,6 +109,7 @@ typedef struct kstat_s {
         void             *ks_private;               /* private data */
         kmutex_t         ks_lock;                   /* kstat data lock */
         struct list_head ks_list;                   /* kstat linkage */
+       kstat_module_t   *ks_owner;                 /* kstat module linkage */
 } kstat_t;
 
 typedef struct kstat_named_s {
index 2e55901012888c111ea6f6f4ba1bc21d6795788a..4e900c066a41eb076cee71a5e8dd9fb3d09990c6 100644 (file)
@@ -37,8 +37,8 @@
 #define PDE_DATA(x) (PDE(x)->data)
 #endif
 
-static spinlock_t kstat_lock;
-static struct list_head kstat_list;
+static kmutex_t kstat_module_lock;
+static struct list_head kstat_module_list;
 static kid_t kstat_id;
 
 static void
@@ -351,6 +351,47 @@ static struct seq_operations kstat_seq_ops = {
         .stop  = kstat_seq_stop,
 };
 
+static kstat_module_t *
+kstat_find_module(char *name)
+{
+       kstat_module_t *module;
+
+       list_for_each_entry(module, &kstat_module_list, ksm_module_list)
+               if (strncmp(name, module->ksm_name, KSTAT_STRLEN) == 0)
+                       return (module);
+
+       return (NULL);
+}
+
+static kstat_module_t *
+kstat_create_module(char *name)
+{
+       kstat_module_t *module;
+       struct proc_dir_entry *pde;
+
+       pde = proc_mkdir(name, proc_spl_kstat);
+       if (pde == NULL)
+               return (NULL);
+
+       module = kmem_alloc(sizeof (kstat_module_t), KM_SLEEP);
+       module->ksm_proc = pde;
+       strlcpy(module->ksm_name, name, KSTAT_STRLEN+1);
+       INIT_LIST_HEAD(&module->ksm_kstat_list);
+       list_add_tail(&module->ksm_module_list, &kstat_module_list);
+
+       return (module);
+
+}
+
+static void
+kstat_delete_module(kstat_module_t *module)
+{
+       ASSERT(list_empty(&module->ksm_kstat_list));
+       remove_proc_entry(module->ksm_name, proc_spl_kstat);
+       list_del(&module->ksm_module_list);
+       kmem_free(module, sizeof(kstat_module_t));
+}
+
 static int
 proc_kstat_open(struct inode *inode, struct file *filp)
 {
@@ -393,10 +434,10 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
        if (ksp == NULL)
                return ksp;
 
-       spin_lock(&kstat_lock);
+       mutex_enter(&kstat_module_lock);
        ksp->ks_kid = kstat_id;
         kstat_id++;
-       spin_unlock(&kstat_lock);
+       mutex_exit(&kstat_module_lock);
 
         ksp->ks_magic = KS_MAGIC;
        mutex_init(&ksp->ks_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -459,71 +500,64 @@ EXPORT_SYMBOL(__kstat_create);
 void
 __kstat_install(kstat_t *ksp)
 {
-       struct proc_dir_entry *de_module, *de_name;
+       kstat_module_t *module;
        kstat_t *tmp;
-       int rc = 0;
-       SENTRY;
-
-       spin_lock(&kstat_lock);
 
-       /* Item may only be added to the list once */
-        list_for_each_entry(tmp, &kstat_list, ks_list) {
-                if (tmp == ksp) {
-                       spin_unlock(&kstat_lock);
-                       SGOTO(out, rc = -EEXIST);
-               }
-       }
+       ASSERT(ksp);
 
-        list_add_tail(&ksp->ks_list, &kstat_list);
-       spin_unlock(&kstat_lock);
+       mutex_enter(&kstat_module_lock);
 
-       de_module = proc_dir_entry_find(proc_spl_kstat, ksp->ks_module);
-       if (de_module == NULL) {
-                de_module = proc_mkdir(ksp->ks_module, proc_spl_kstat);
-               if (de_module == NULL)
-                       SGOTO(out, rc = -EUNATCH);
+       module = kstat_find_module(ksp->ks_module);
+       if (module == NULL) {
+               module = kstat_create_module(ksp->ks_module);
+               if (module == NULL)
+                       goto out;
        }
 
-       de_name = create_proc_entry(ksp->ks_name, 0444, de_module);
-       if (de_name == NULL)
-               SGOTO(out, rc = -EUNATCH);
+       /*
+        * Only one entry by this name per-module, on failure the module
+        * shouldn't be deleted because we know it has at least one entry.
+        */
+       list_for_each_entry(tmp, &module->ksm_kstat_list, ks_list)
+               if (strncmp(tmp->ks_name, ksp->ks_name, KSTAT_STRLEN) == 0)
+                       goto out;
+
+       list_add_tail(&ksp->ks_list, &module->ksm_kstat_list);
 
        mutex_enter(&ksp->ks_lock);
-       ksp->ks_proc = de_name;
-       de_name->proc_fops = &proc_kstat_operations;
-        de_name->data = (void *)ksp;
+       ksp->ks_owner = module;
+       ksp->ks_proc = proc_create_data(ksp->ks_name, 0444,
+           module->ksm_proc, &proc_kstat_operations, (void *)ksp);
+       if (ksp->ks_proc == NULL) {
+               list_del_init(&ksp->ks_list);
+               if (list_empty(&module->ksm_kstat_list))
+                       kstat_delete_module(module);
+       }
        mutex_exit(&ksp->ks_lock);
 out:
-       if (rc) {
-               spin_lock(&kstat_lock);
-               list_del_init(&ksp->ks_list);
-               spin_unlock(&kstat_lock);
-       }
-
-       SEXIT;
+       mutex_exit(&kstat_module_lock);
 }
 EXPORT_SYMBOL(__kstat_install);
 
 void
 __kstat_delete(kstat_t *ksp)
 {
-       struct proc_dir_entry *de_module;
+       kstat_module_t *module = ksp->ks_owner;
 
-       spin_lock(&kstat_lock);
-        list_del_init(&ksp->ks_list);
-       spin_unlock(&kstat_lock);
+       mutex_enter(&kstat_module_lock);
+       list_del_init(&ksp->ks_list);
+       mutex_exit(&kstat_module_lock);
 
-        if (ksp->ks_proc) {
-               de_module = ksp->ks_proc->parent;
-               remove_proc_entry(ksp->ks_name, de_module);
+       if (ksp->ks_proc) {
+               remove_proc_entry(ksp->ks_name, module->ksm_proc);
 
-               /* Remove top level module directory if it's empty */
-               if (proc_dir_entries(de_module) == 0)
-                       remove_proc_entry(de_module->name, de_module->parent);
+               /* Remove top level module directory if it's empty */
+               if (list_empty(&module->ksm_kstat_list))
+                       kstat_delete_module(module);
        }
 
        if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
-                kmem_free(ksp->ks_data, ksp->ks_data_size);
+               kmem_free(ksp->ks_data, ksp->ks_data_size);
 
        mutex_destroy(&ksp->ks_lock);
        kmem_free(ksp, sizeof(*ksp));
@@ -536,8 +570,8 @@ int
 spl_kstat_init(void)
 {
        SENTRY;
-       spin_lock_init(&kstat_lock);
-       INIT_LIST_HEAD(&kstat_list);
+       mutex_init(&kstat_module_lock, NULL, MUTEX_DEFAULT, NULL);
+       INIT_LIST_HEAD(&kstat_module_list);
         kstat_id = 0;
        SRETURN(0);
 }
@@ -546,7 +580,8 @@ void
 spl_kstat_fini(void)
 {
        SENTRY;
-       ASSERT(list_empty(&kstat_list));
+       ASSERT(list_empty(&kstat_module_list));
+       mutex_destroy(&kstat_module_lock);
        SEXIT;
 }
 
index cd4fa1b47c5eb987ee7eace7e5178e40760f03ba..b8379d0fe401f858ff1c29e0945c8a0c7ef313d7 100644 (file)
@@ -1120,39 +1120,6 @@ static struct ctl_table spl_root[] = {
        { 0 }
 };
 
-static int
-proc_dir_entry_match(int len, const char *name, struct proc_dir_entry *de)
-{
-        if (de->namelen != len)
-                return 0;
-
-        return !memcmp(name, de->name, len);
-}
-
-struct proc_dir_entry *
-proc_dir_entry_find(struct proc_dir_entry *root, const char *str)
-{
-       struct proc_dir_entry *de;
-
-       for (de = root->subdir; de; de = de->next)
-               if (proc_dir_entry_match(strlen(str), str, de))
-                       return de;
-
-       return NULL;
-}
-
-int
-proc_dir_entries(struct proc_dir_entry *root)
-{
-       struct proc_dir_entry *de;
-       int i = 0;
-
-       for (de = root->subdir; de; de = de->next)
-               i++;
-
-       return i;
-}
-
 int
 spl_proc_init(void)
 {
@@ -1174,11 +1141,11 @@ spl_proc_init(void)
         if (proc_spl_kmem == NULL)
                 SGOTO(out, rc = -EUNATCH);
 
-       proc_spl_kmem_slab = create_proc_entry("slab", 0444, proc_spl_kmem);
+       proc_spl_kmem_slab = proc_create_data("slab", 0444,
+               proc_spl_kmem, &proc_slab_operations, NULL);
         if (proc_spl_kmem_slab == NULL)
                SGOTO(out, rc = -EUNATCH);
 
-        proc_spl_kmem_slab->proc_fops = &proc_slab_operations;
 #endif /* DEBUG_KMEM */
 
         proc_spl_kstat = proc_mkdir("kstat", proc_spl);