* [Linux-ia64] [PATCH] efivars.c spinlock patch
@ 2003-03-14 22:36 Matt Domsch
0 siblings, 0 replies; only message in thread
From: Matt Domsch @ 2003-03-14 22:36 UTC (permalink / raw)
To: linux-ia64
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);
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2003-03-14 22:36 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-14 22:36 [Linux-ia64] [PATCH] efivars.c spinlock patch Matt Domsch
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.