[Linux-ia64] [PATCH] efivars.c spinlock patch

From: Matt Domsch <Matt_Domsch_at_Dell.com>
Date: 2003-03-15 09:36:54
Bjorn, this was submitted back in December, though I couldn't test it 
then, but seeing Jes's question/answer about CONFIG_IA64_MCA this week 
gave me the fix I needed to test this.

Below is a patch, in 2.5.x since December, to fix the locking in
efivars.c.  Thanks to Peter Chubb for finding and suggesting the fixes.

Tested against BK-current ia64 2.4.21-pre5.  Please apply.

Thanks,
Matt

-- 
Matt Domsch
Sr. Software Engineer, Lead Engineer
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com

--- linux-ia64-2.4/arch/ia64/kernel/efivars.c	Fri Mar 14 11:51:22 2003
+++ linux-2.4-ia64-work/arch/ia64/kernel/efivars.c	Fri Mar 14 13:31:28 2003
@@ -29,6 +29,9 @@
  *
  * Changelog:
  *
+ *  10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com>
+ *   fix locking per Peter Chubb's findings
+ * 
  *  25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com>
  *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse()
  *
@@ -73,7 +76,7 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@
 MODULE_DESCRIPTION("/proc interface to EFI Variables");
 MODULE_LICENSE("GPL");
 
-#define EFIVARS_VERSION "0.05 2002-Mar-26"
+#define EFIVARS_VERSION "0.06 2002-Dec-10"
 
 static int
 efivar_read(char *page, char **start, off_t off,
@@ -106,6 +109,14 @@ typedef struct _efivar_entry_t {
 	struct list_head        list;
 } efivar_entry_t;
 
+/*
+  efivars_lock protects two things:
+  1) efivar_list - adds, removals, reads, writes
+  2) efi.[gs]et_variable() calls.
+  It must not be held when creating proc entries or calling kmalloc.
+  efi.get_next_variable() is only called from efivars_init(),
+  which is protected by the BKL, so that path is safe.
+*/
 static spinlock_t efivars_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(efivar_list);
 static struct proc_dir_entry *efi_vars_dir = NULL;
@@ -150,6 +161,7 @@ proc_calc_metrics(char *page, char **sta
  *    variable_name_size = number of bytes required to hold
  *                         variable_name (not counting the NULL
  *                         character at the end.
+ *    efivars_lock is not held on entry or exit.
  * Returns 1 on failure, 0 on success
  */
 static int
@@ -160,10 +172,12 @@ efivar_create_proc_entry(unsigned long v
 
 	int i, short_name_size = variable_name_size /
 		sizeof(efi_char16_t) + 38;
-	char *short_name = kmalloc(short_name_size+1,
-				   GFP_KERNEL);
-	efivar_entry_t *new_efivar = kmalloc(sizeof(efivar_entry_t),
-					     GFP_KERNEL);
+	char *short_name;
+	efivar_entry_t *new_efivar;
+
+        short_name = kmalloc(short_name_size+1, GFP_KERNEL);
+        new_efivar = kmalloc(sizeof(efivar_entry_t), GFP_KERNEL);
+
 	if (!short_name || !new_efivar)  {
 		if (short_name)        kfree(short_name);
 		if (new_efivar)        kfree(new_efivar);
@@ -188,19 +202,18 @@ efivar_create_proc_entry(unsigned long v
 	*(short_name + strlen(short_name)) = '-';
 	efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
 
-
 	/* Create the entry in proc */
 	new_efivar->entry = create_proc_entry(short_name, 0600, efi_vars_dir);
 	kfree(short_name); short_name = NULL;
 	if (!new_efivar->entry) return 1;
 
-
 	new_efivar->entry->data = new_efivar;
 	new_efivar->entry->read_proc = efivar_read;
 	new_efivar->entry->write_proc = efivar_write;
 
+        spin_lock(&efivars_lock);
 	list_add(&new_efivar->list, &efivar_list);
-
+        spin_unlock(&efivars_lock);
 
 	return 0;
 }
@@ -326,6 +339,8 @@ efivar_write(struct file *file, const ch
 		kfree(efivar);
 	}
 
+	spin_unlock(&efivars_lock);
+
 	/* If this is a new variable, set up the proc entry for it. */
 	if (!found) {
 		efivar_create_proc_entry(utf8_strsize(var_data->VariableName,
@@ -336,7 +351,6 @@ efivar_write(struct file *file, const ch
 
 	kfree(var_data);
 	MOD_DEC_USE_COUNT;
-	spin_unlock(&efivars_lock);
 	return size;
 }
 
@@ -351,8 +365,6 @@ efivars_init(void)
 	efi_char16_t *variable_name = kmalloc(1024, GFP_KERNEL);
 	unsigned long variable_name_size = 1024;
 
-	spin_lock(&efivars_lock);
-
 	printk(KERN_INFO "EFI Variables Facility v%s\n", EFIVARS_VERSION);
 
         /* Since efi.c happens before procfs is available,
@@ -365,8 +377,6 @@ efivars_init(void)
 
 	efi_vars_dir = proc_mkdir("vars", efi_dir);
 
-
-
 	/* Per EFI spec, the maximum storage allocated for both
 	   the variable name and variable data is 1024 bytes.
 	*/
@@ -398,7 +408,6 @@ efivars_init(void)
 	} while (status != EFI_NOT_FOUND);
 
 	kfree(variable_name);
-	spin_unlock(&efivars_lock);
 	return 0;
 }
 
@@ -408,17 +417,16 @@ efivars_exit(void)
 	struct list_head *pos, *n;
 	efivar_entry_t *efivar;
 
-	spin_lock(&efivars_lock);
-
+        spin_lock(&efivars_lock);
 	list_for_each_safe(pos, n, &efivar_list) {
 		efivar = efivar_entry(pos);
 		remove_proc_entry(efivar->entry->name, efi_vars_dir);
 		list_del(&efivar->list);
 		kfree(efivar);
 	}
-	remove_proc_entry(efi_vars_dir->name, efi_dir);
 	spin_unlock(&efivars_lock);
 
+	remove_proc_entry(efi_vars_dir->name, efi_dir);
 }
 
 module_init(efivars_init);
Received on Fri Mar 14 14:37:02 2003

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:12 EST