]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.8-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2013 22:02:37 +0000 (15:02 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2013 22:02:37 +0000 (15:02 -0700)
added patches:
efi-be-more-paranoid-about-available-space-when-creating-variables.patch
efivars-disable-external-interrupt-while-holding-efivars-lock.patch
usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
usb-fix-connected-device-switch-to-inactive-state.patch
usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
usb-rip-out-recursive-call-on-warm-port-reset.patch

queue-3.8/efi-be-more-paranoid-about-available-space-when-creating-variables.patch [new file with mode: 0644]
queue-3.8/efivars-disable-external-interrupt-while-holding-efivars-lock.patch [new file with mode: 0644]
queue-3.8/series
queue-3.8/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch [new file with mode: 0644]
queue-3.8/usb-fix-connected-device-switch-to-inactive-state.patch [new file with mode: 0644]
queue-3.8/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch [new file with mode: 0644]
queue-3.8/usb-rip-out-recursive-call-on-warm-port-reset.patch [new file with mode: 0644]

diff --git a/queue-3.8/efi-be-more-paranoid-about-available-space-when-creating-variables.patch b/queue-3.8/efi-be-more-paranoid-about-available-space-when-creating-variables.patch
new file mode 100644 (file)
index 0000000..4b68a89
--- /dev/null
@@ -0,0 +1,202 @@
+From 68d929862e29a8b52a7f2f2f86a0600423b093cd Mon Sep 17 00:00:00 2001
+From: Matthew Garrett <matthew.garrett@nebula.com>
+Date: Sat, 2 Mar 2013 19:40:17 -0500
+Subject: efi: be more paranoid about available space when creating variables
+
+From: Matthew Garrett <matthew.garrett@nebula.com>
+
+commit 68d929862e29a8b52a7f2f2f86a0600423b093cd upstream.
+
+UEFI variables are typically stored in flash. For various reasons, avaiable
+space is typically not reclaimed immediately upon the deletion of a
+variable - instead, the system will garbage collect during initialisation
+after a reboot.
+
+Some systems appear to handle this garbage collection extremely poorly,
+failing if more than 50% of the system flash is in use. This can result in
+the machine refusing to boot. The safest thing to do for the moment is to
+forbid writes if they'd end up using more than half of the storage space.
+We can make this more finegrained later if we come up with a method for
+identifying the broken machines.
+
+Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
+Cc: Josh Boyer <jwboyer@redhat.com>
+Signed-off-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/efivars.c |  106 +++++++++++++++++++++++++++++++++------------
+ 1 file changed, 79 insertions(+), 27 deletions(-)
+
+--- a/drivers/firmware/efivars.c
++++ b/drivers/firmware/efivars.c
+@@ -419,6 +419,44 @@ get_var_data(struct efivars *efivars, st
+       return status;
+ }
++static efi_status_t
++check_var_size_locked(struct efivars *efivars, u32 attributes,
++                      unsigned long size)
++{
++      u64 storage_size, remaining_size, max_size;
++      efi_status_t status;
++      const struct efivar_operations *fops = efivars->ops;
++
++      if (!efivars->ops->query_variable_info)
++              return EFI_UNSUPPORTED;
++
++      status = fops->query_variable_info(attributes, &storage_size,
++                                         &remaining_size, &max_size);
++
++      if (status != EFI_SUCCESS)
++              return status;
++
++      if (!storage_size || size > remaining_size || size > max_size ||
++          (remaining_size - size) < (storage_size / 2))
++              return EFI_OUT_OF_RESOURCES;
++
++      return status;
++}
++
++
++static efi_status_t
++check_var_size(struct efivars *efivars, u32 attributes, unsigned long size)
++{
++      efi_status_t status;
++      unsigned long flags;
++
++      spin_lock_irqsave(&efivars->lock, flags);
++      status = check_var_size_locked(efivars, attributes, size);
++      spin_unlock_irqrestore(&efivars->lock, flags);
++
++      return status;
++}
++
+ static ssize_t
+ efivar_guid_read(struct efivar_entry *entry, char *buf)
+ {
+@@ -540,11 +578,16 @@ efivar_store_raw(struct efivar_entry *en
+       }
+       spin_lock_irq(&efivars->lock);
+-      status = efivars->ops->set_variable(new_var->VariableName,
+-                                          &new_var->VendorGuid,
+-                                          new_var->Attributes,
+-                                          new_var->DataSize,
+-                                          new_var->Data);
++
++      status = check_var_size_locked(efivars, new_var->Attributes,
++             new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));
++
++      if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED)
++              status = efivars->ops->set_variable(new_var->VariableName,
++                                                  &new_var->VendorGuid,
++                                                  new_var->Attributes,
++                                                  new_var->DataSize,
++                                                  new_var->Data);
+       spin_unlock_irq(&efivars->lock);
+@@ -695,8 +738,7 @@ static ssize_t efivarfs_file_write(struc
+       u32 attributes;
+       struct inode *inode = file->f_mapping->host;
+       unsigned long datasize = count - sizeof(attributes);
+-      unsigned long newdatasize;
+-      u64 storage_size, remaining_size, max_size;
++      unsigned long newdatasize, varsize;
+       ssize_t bytes = 0;
+       if (count < sizeof(attributes))
+@@ -715,28 +757,18 @@ static ssize_t efivarfs_file_write(struc
+        * amounts of memory. Pick a default size of 64K if
+        * QueryVariableInfo() isn't supported by the firmware.
+        */
+-      spin_lock_irq(&efivars->lock);
+-      if (!efivars->ops->query_variable_info)
+-              status = EFI_UNSUPPORTED;
+-      else {
+-              const struct efivar_operations *fops = efivars->ops;
+-              status = fops->query_variable_info(attributes, &storage_size,
+-                                                 &remaining_size, &max_size);
+-      }
+-
+-      spin_unlock_irq(&efivars->lock);
++      varsize = datasize + utf16_strsize(var->var.VariableName, 1024);
++      status = check_var_size(efivars, attributes, varsize);
+       if (status != EFI_SUCCESS) {
+               if (status != EFI_UNSUPPORTED)
+                       return efi_status_to_err(status);
+-              remaining_size = 65536;
++              if (datasize > 65536)
++                      return -ENOSPC;
+       }
+-      if (datasize > remaining_size)
+-              return -ENOSPC;
+-
+       data = kmalloc(datasize, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+@@ -758,6 +790,19 @@ static ssize_t efivarfs_file_write(struc
+        */
+       spin_lock_irq(&efivars->lock);
++      /*
++       * Ensure that the available space hasn't shrunk below the safe level
++       */
++
++      status = check_var_size_locked(efivars, attributes, varsize);
++
++      if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
++              spin_unlock_irq(&efivars->lock);
++              kfree(data);
++
++              return efi_status_to_err(status);
++      }
++
+       status = efivars->ops->set_variable(var->var.VariableName,
+                                           &var->var.VendorGuid,
+                                           attributes, datasize,
+@@ -1348,7 +1393,6 @@ static int efi_pstore_write(enum pstore_
+       efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+       struct efivars *efivars = psi->data;
+       int i, ret = 0;
+-      u64 storage_space, remaining_space, max_variable_size;
+       efi_status_t status = EFI_NOT_FOUND;
+       unsigned long flags;
+@@ -1359,11 +1403,11 @@ static int efi_pstore_write(enum pstore_
+        * size: a size of logging data
+        * DUMP_NAME_LEN * 2: a maximum size of variable name
+        */
+-      status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES,
+-                                                 &storage_space,
+-                                                 &remaining_space,
+-                                                 &max_variable_size);
+-      if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
++
++      status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES,
++                                       size + DUMP_NAME_LEN * 2);
++
++      if (status) {
+               spin_unlock_irqrestore(&efivars->lock, flags);
+               *id = part;
+               return -ENOSPC;
+@@ -1541,6 +1585,14 @@ static ssize_t efivar_create(struct file
+               return -EINVAL;
+       }
++      status = check_var_size_locked(efivars, new_var->Attributes,
++             new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));
++
++      if (status && status != EFI_UNSUPPORTED) {
++              spin_unlock_irq(&efivars->lock);
++              return efi_status_to_err(status);
++      }
++
+       /* now *really* create the variable via EFI */
+       status = efivars->ops->set_variable(new_var->VariableName,
+                                           &new_var->VendorGuid,
diff --git a/queue-3.8/efivars-disable-external-interrupt-while-holding-efivars-lock.patch b/queue-3.8/efivars-disable-external-interrupt-while-holding-efivars-lock.patch
new file mode 100644 (file)
index 0000000..04c35d1
--- /dev/null
@@ -0,0 +1,383 @@
+From 81fa4e581d9283f7992a0d8c534bb141eb840a14 Mon Sep 17 00:00:00 2001
+From: Seiji Aguchi <seiji.aguchi@hds.com>
+Date: Tue, 12 Feb 2013 12:59:07 -0800
+Subject: efivars: Disable external interrupt while holding efivars->lock
+
+From: Seiji Aguchi <seiji.aguchi@hds.com>
+
+commit 81fa4e581d9283f7992a0d8c534bb141eb840a14 upstream.
+
+[Problem]
+There is a scenario which efi_pstore fails to log messages in a panic case.
+
+ - CPUA holds an efi_var->lock in either efivarfs parts
+   or efi_pstore with interrupt enabled.
+ - CPUB panics and sends IPI to CPUA in smp_send_stop().
+ - CPUA stops with holding the lock.
+ - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC)
+   but it returns without logging messages.
+
+[Patch Description]
+This patch disables an external interruption while holding efivars->lock
+as follows.
+
+In efi_pstore_write() and get_var_data(), spin_lock/spin_unlock is
+replaced by spin_lock_irqsave/spin_unlock_irqrestore because they may
+be called in an interrupt context.
+
+In other functions, they are replaced by spin_lock_irq/spin_unlock_irq.
+because they are all called from a process context.
+
+By applying this patch, we can avoid the problem above with
+a following senario.
+
+ - CPUA holds an efi_var->lock with interrupt disabled.
+ - CPUB panics and sends IPI to CPUA in smp_send_stop().
+ - CPUA receives the IPI after releasing the lock because it is
+   disabling interrupt while holding the lock.
+ - CPUB waits for one sec until CPUA releases the lock.
+ - CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC)
+   And it can hold the lock successfully.
+
+Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
+Acked-by: Mike Waychison <mikew@google.com>
+Acked-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Tony Luck <tony.luck@intel.com>
+Cc: Josh Boyer <jwboyer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/firmware/efivars.c |   84 +++++++++++++++++++++++----------------------
+ 1 file changed, 43 insertions(+), 41 deletions(-)
+
+--- a/drivers/firmware/efivars.c
++++ b/drivers/firmware/efivars.c
+@@ -406,10 +406,11 @@ static efi_status_t
+ get_var_data(struct efivars *efivars, struct efi_variable *var)
+ {
+       efi_status_t status;
++      unsigned long flags;
+-      spin_lock(&efivars->lock);
++      spin_lock_irqsave(&efivars->lock, flags);
+       status = get_var_data_locked(efivars, var);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irqrestore(&efivars->lock, flags);
+       if (status != EFI_SUCCESS) {
+               printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
+@@ -538,14 +539,14 @@ efivar_store_raw(struct efivar_entry *en
+               return -EINVAL;
+       }
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       status = efivars->ops->set_variable(new_var->VariableName,
+                                           &new_var->VendorGuid,
+                                           new_var->Attributes,
+                                           new_var->DataSize,
+                                           new_var->Data);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       if (status != EFI_SUCCESS) {
+               printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
+@@ -714,7 +715,7 @@ static ssize_t efivarfs_file_write(struc
+        * amounts of memory. Pick a default size of 64K if
+        * QueryVariableInfo() isn't supported by the firmware.
+        */
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       if (!efivars->ops->query_variable_info)
+               status = EFI_UNSUPPORTED;
+@@ -724,7 +725,7 @@ static ssize_t efivarfs_file_write(struc
+                                                  &remaining_size, &max_size);
+       }
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       if (status != EFI_SUCCESS) {
+               if (status != EFI_UNSUPPORTED)
+@@ -755,7 +756,7 @@ static ssize_t efivarfs_file_write(struc
+        * set_variable call, and removal of the variable from the efivars
+        * list (in the case of an authenticated delete).
+        */
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       status = efivars->ops->set_variable(var->var.VariableName,
+                                           &var->var.VendorGuid,
+@@ -763,7 +764,7 @@ static ssize_t efivarfs_file_write(struc
+                                           data);
+       if (status != EFI_SUCCESS) {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               kfree(data);
+               return efi_status_to_err(status);
+@@ -784,21 +785,21 @@ static ssize_t efivarfs_file_write(struc
+                                           NULL);
+       if (status == EFI_BUFFER_TOO_SMALL) {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               mutex_lock(&inode->i_mutex);
+               i_size_write(inode, newdatasize + sizeof(attributes));
+               mutex_unlock(&inode->i_mutex);
+       } else if (status == EFI_NOT_FOUND) {
+               list_del(&var->list);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               efivar_unregister(var);
+               drop_nlink(inode);
+               d_delete(file->f_dentry);
+               dput(file->f_dentry);
+       } else {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               pr_warn("efivarfs: inconsistent EFI variable implementation? "
+                               "status = %lx\n", status);
+       }
+@@ -820,11 +821,11 @@ static ssize_t efivarfs_file_read(struct
+       void *data;
+       ssize_t size = 0;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       status = efivars->ops->get_variable(var->var.VariableName,
+                                           &var->var.VendorGuid,
+                                           &attributes, &datasize, NULL);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       if (status != EFI_BUFFER_TOO_SMALL)
+               return efi_status_to_err(status);
+@@ -834,12 +835,12 @@ static ssize_t efivarfs_file_read(struct
+       if (!data)
+               return -ENOMEM;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       status = efivars->ops->get_variable(var->var.VariableName,
+                                           &var->var.VendorGuid,
+                                           &attributes, &datasize,
+                                           (data + sizeof(attributes)));
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       if (status != EFI_SUCCESS) {
+               size = efi_status_to_err(status);
+@@ -1005,9 +1006,9 @@ static int efivarfs_create(struct inode
+               goto out;
+       kobject_uevent(&var->kobj, KOBJ_ADD);
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       list_add(&var->list, &efivars->list);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       d_instantiate(dentry, inode);
+       dget(dentry);
+ out:
+@@ -1024,7 +1025,7 @@ static int efivarfs_unlink(struct inode
+       struct efivars *efivars = var->efivars;
+       efi_status_t status;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       status = efivars->ops->set_variable(var->var.VariableName,
+                                           &var->var.VendorGuid,
+@@ -1032,14 +1033,14 @@ static int efivarfs_unlink(struct inode
+       if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
+               list_del(&var->list);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               efivar_unregister(var);
+               drop_nlink(dentry->d_inode);
+               dput(dentry);
+               return 0;
+       }
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       return -EINVAL;
+ };
+@@ -1194,13 +1195,13 @@ static int efivarfs_fill_super(struct su
+               /* copied by the above to local storage in the dentry. */
+               kfree(name);
+-              spin_lock(&efivars->lock);
++              spin_lock_irq(&efivars->lock);
+               efivars->ops->get_variable(entry->var.VariableName,
+                                          &entry->var.VendorGuid,
+                                          &entry->var.Attributes,
+                                          &size,
+                                          NULL);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               mutex_lock(&inode->i_mutex);
+               inode->i_private = entry;
+@@ -1263,7 +1264,7 @@ static int efi_pstore_open(struct pstore
+ {
+       struct efivars *efivars = psi->data;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       efivars->walk_entry = list_first_entry(&efivars->list,
+                                              struct efivar_entry, list);
+       return 0;
+@@ -1273,7 +1274,7 @@ static int efi_pstore_close(struct pstor
+ {
+       struct efivars *efivars = psi->data;
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       return 0;
+ }
+@@ -1349,8 +1350,9 @@ static int efi_pstore_write(enum pstore_
+       int i, ret = 0;
+       u64 storage_space, remaining_space, max_variable_size;
+       efi_status_t status = EFI_NOT_FOUND;
++      unsigned long flags;
+-      spin_lock(&efivars->lock);
++      spin_lock_irqsave(&efivars->lock, flags);
+       /*
+        * Check if there is a space enough to log.
+@@ -1362,7 +1364,7 @@ static int efi_pstore_write(enum pstore_
+                                                  &remaining_space,
+                                                  &max_variable_size);
+       if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irqrestore(&efivars->lock, flags);
+               *id = part;
+               return -ENOSPC;
+       }
+@@ -1376,7 +1378,7 @@ static int efi_pstore_write(enum pstore_
+       efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
+                                  size, psi->buf);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irqrestore(&efivars->lock, flags);
+       if (size)
+               ret = efivar_create_sysfs_entry(efivars,
+@@ -1403,7 +1405,7 @@ static int efi_pstore_erase(enum pstore_
+       sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
+               time.tv_sec);
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       for (i = 0; i < DUMP_NAME_LEN; i++)
+               efi_name[i] = name[i];
+@@ -1447,7 +1449,7 @@ static int efi_pstore_erase(enum pstore_
+       if (found)
+               list_del(&found->list);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       if (found)
+               efivar_unregister(found);
+@@ -1517,7 +1519,7 @@ static ssize_t efivar_create(struct file
+               return -EINVAL;
+       }
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       /*
+        * Does this variable already exist?
+@@ -1535,7 +1537,7 @@ static ssize_t efivar_create(struct file
+               }
+       }
+       if (found) {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               return -EINVAL;
+       }
+@@ -1549,10 +1551,10 @@ static ssize_t efivar_create(struct file
+       if (status != EFI_SUCCESS) {
+               printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
+                       status);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               return -EIO;
+       }
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       /* Create the entry in sysfs.  Locking is not required here */
+       status = efivar_create_sysfs_entry(efivars,
+@@ -1580,7 +1582,7 @@ static ssize_t efivar_delete(struct file
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       /*
+        * Does this variable already exist?
+@@ -1598,7 +1600,7 @@ static ssize_t efivar_delete(struct file
+               }
+       }
+       if (!found) {
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               return -EINVAL;
+       }
+       /* force the Attributes/DataSize to 0 to ensure deletion */
+@@ -1614,12 +1616,12 @@ static ssize_t efivar_delete(struct file
+       if (status != EFI_SUCCESS) {
+               printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
+                       status);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               return -EIO;
+       }
+       list_del(&search_efivar->list);
+       /* We need to release this lock before unregistering. */
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       efivar_unregister(search_efivar);
+       /* It's dead Jim.... */
+@@ -1734,9 +1736,9 @@ efivar_create_sysfs_entry(struct efivars
+       kfree(short_name);
+       short_name = NULL;
+-      spin_lock(&efivars->lock);
++      spin_lock_irq(&efivars->lock);
+       list_add(&new_efivar->list, &efivars->list);
+-      spin_unlock(&efivars->lock);
++      spin_unlock_irq(&efivars->lock);
+       return 0;
+ }
+@@ -1805,9 +1807,9 @@ void unregister_efivars(struct efivars *
+       struct efivar_entry *entry, *n;
+       list_for_each_entry_safe(entry, n, &efivars->list, list) {
+-              spin_lock(&efivars->lock);
++              spin_lock_irq(&efivars->lock);
+               list_del(&entry->list);
+-              spin_unlock(&efivars->lock);
++              spin_unlock_irq(&efivars->lock);
+               efivar_unregister(entry);
+       }
+       if (efivars->new_var)
index 039df3cc33363e34a3bbd217f0953c74ce6cafd7..f5da0f53e621964af30c3bd35c7b76ce4401746d 100644 (file)
@@ -90,3 +90,9 @@ arm-mxs_defconfig-make-usb-host-functional-again.patch
 arm-kirkwood-of_serial-fix-clock-gating-by-removing-clock-frequency.patch
 powerpc-apply-early-paca-fixups-to-boot_paca-and-the-boot-cpu-s-paca.patch
 ftrace-update-the-kconfig-for-dynamic_ftrace.patch
+efivars-disable-external-interrupt-while-holding-efivars-lock.patch
+efi-be-more-paranoid-about-available-space-when-creating-variables.patch
+usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
+usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
+usb-rip-out-recursive-call-on-warm-port-reset.patch
+usb-fix-connected-device-switch-to-inactive-state.patch
diff --git a/queue-3.8/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch b/queue-3.8/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
new file mode 100644 (file)
index 0000000..b859916
--- /dev/null
@@ -0,0 +1,66 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 14:57:57 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:23:30 -0800
+Subject: USB: Don't use EHCI port sempahore for USB 3.0 hubs.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002330.GA13281@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 0fe51aa5eee51db7c7ecd201d42a977ad79c58b6.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 65bdac5effd15d6af619b3b7218627ef4d84ed6a "USB: Handle warm
+reset failure on empty port."]
+
+The EHCI host controller needs to prevent EHCI initialization when the
+UHCI or OHCI companion controller is in the middle of a port reset.  It
+uses ehci_cf_port_reset_rwsem to do this.  USB 3.0 hubs can't be under
+an EHCI host controller, so it makes no sense to down the semaphore for
+USB 3.0 hubs.  It also makes the warm port reset code more complex.
+
+Don't down ehci_cf_port_reset_rwsem for USB 3.0 hubs.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |   15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2661,17 +2661,16 @@ static int hub_port_reset(struct usb_hub
+ {
+       int i, status;
+-      if (!warm) {
+-              /* Block EHCI CF initialization during the port reset.
+-               * Some companion controllers don't like it when they mix.
+-               */
+-              down_read(&ehci_cf_port_reset_rwsem);
+-      } else {
+-              if (!hub_is_superspeed(hub->hdev)) {
++      if (!hub_is_superspeed(hub->hdev)) {
++              if (warm) {
+                       dev_err(hub->intfdev, "only USB3 hub support "
+                                               "warm reset\n");
+                       return -EINVAL;
+               }
++              /* Block EHCI CF initialization during the port reset.
++               * Some companion controllers don't like it when they mix.
++               */
++              down_read(&ehci_cf_port_reset_rwsem);
+       }
+       /* Reset the port */
+@@ -2709,7 +2708,7 @@ static int hub_port_reset(struct usb_hub
+               port1);
+ done:
+-      if (!warm)
++      if (!hub_is_superspeed(hub->hdev))
+               up_read(&ehci_cf_port_reset_rwsem);
+       return status;
diff --git a/queue-3.8/usb-fix-connected-device-switch-to-inactive-state.patch b/queue-3.8/usb-fix-connected-device-switch-to-inactive-state.patch
new file mode 100644 (file)
index 0000000..ffe0c81
--- /dev/null
@@ -0,0 +1,91 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 14:59:14 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:23:43 -0800
+Subject: USB: Fix connected device switch to Inactive state.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002343.GA13329@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit d3b9d7a9051d7024a93c76a84b2f84b3b66ad6d5.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 65bdac5effd15d6af619b3b7218627ef4d84ed6a "USB: Handle warm
+reset failure on empty port."]
+
+A USB 3.0 device can transition to the Inactive state if a U1 or U2 exit
+transition fails.  The current code in hub_events simply issues a warm
+reset, but does not call any pre-reset or post-reset driver methods (or
+unbind/rebind drivers without them).  Therefore the drivers won't know
+their device has just been reset.
+
+hub_events should instead call usb_reset_device.  This means
+hub_port_reset now needs to figure out whether it should issue a warm
+reset or a hot reset.
+
+Remove the FIXME note about needing disconnect() for a NOTATTACHED
+device.  This patch fixes that.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |   30 +++++++++++++++++++++++++-----
+ 1 file changed, 25 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2605,7 +2605,6 @@ static void hub_port_finish_reset(struct
+       case -ENODEV:
+               clear_port_feature(hub->hdev,
+                               port1, USB_PORT_FEAT_C_RESET);
+-              /* FIXME need disconnect() for NOTATTACHED device */
+               if (hub_is_superspeed(hub->hdev)) {
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+@@ -2639,6 +2638,18 @@ static int hub_port_reset(struct usb_hub
+                * Some companion controllers don't like it when they mix.
+                */
+               down_read(&ehci_cf_port_reset_rwsem);
++      } else if (!warm) {
++              /*
++               * If the caller hasn't explicitly requested a warm reset,
++               * double check and see if one is needed.
++               */
++              status = hub_port_status(hub, port1,
++                                      &portstatus, &portchange);
++              if (status < 0)
++                      goto done;
++
++              if (hub_port_warm_reset_required(hub, portstatus))
++                      warm = true;
+       }
+       /* Reset the port */
+@@ -4730,12 +4741,21 @@ static void hub_events(void)
+                        */
+                       if (hub_port_warm_reset_required(hub, portstatus)) {
+                               int status;
++                              struct usb_device *udev =
++                                      hub->ports[i - 1]->child;
+                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+-                              status = hub_port_reset(hub, i, NULL,
+-                                              HUB_BH_RESET_TIME, true);
+-                              if (status < 0)
+-                                      hub_port_disable(hub, i, 1);
++                              if (!udev) {
++                                      status = hub_port_reset(hub, i,
++                                                      NULL, HUB_BH_RESET_TIME,
++                                                      true);
++                                      if (status < 0)
++                                              hub_port_disable(hub, i, 1);
++                              } else {
++                                      usb_lock_device(udev);
++                                      status = usb_reset_device(udev);
++                                      usb_unlock_device(udev);
++                              }
+                               connect_change = 0;
+                       }
diff --git a/queue-3.8/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch b/queue-3.8/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
new file mode 100644 (file)
index 0000000..32a7545
--- /dev/null
@@ -0,0 +1,75 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 14:58:36 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:23:34 -0800
+Subject: USB: Prepare for refactoring by adding extra udev checks.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002334.GA13297@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 2d4fa940f99663c82ba55b2244638833b388e4e2.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 65bdac5effd15d6af619b3b7218627ef4d84ed6a "USB: Handle warm
+reset failure on empty port."]
+
+The next patch will refactor the hub port code to rip out the recursive
+call to hub_port_reset on a failed hot reset.  In preparation for that,
+make sure all code paths can deal with being called with a NULL udev.
+The usb_device will not be valid if warm reset was issued because a port
+transitioned to the Inactive or Compliance Mode on a device connect.
+
+This patch should have no effect on current behavior.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |   21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2584,6 +2584,9 @@ static int hub_port_wait_reset(struct us
+                               return -ENOTCONN;
+                       if ((portstatus & USB_PORT_STAT_ENABLE)) {
++                              if (!udev)
++                                      return 0;
++
+                               if (hub_is_wusb(hub))
+                                       udev->speed = USB_SPEED_WIRELESS;
+                               else if (hub_is_superspeed(hub->hdev))
+@@ -2627,13 +2630,15 @@ static void hub_port_finish_reset(struct
+                       struct usb_hcd *hcd;
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       msleep(10 + 40);
+-                      update_devnum(udev, 0);
+-                      hcd = bus_to_hcd(udev->bus);
+-                      /* The xHC may think the device is already reset,
+-                       * so ignore the status.
+-                       */
+-                      if (hcd->driver->reset_device)
+-                              hcd->driver->reset_device(hcd, udev);
++                      if (udev) {
++                              update_devnum(udev, 0);
++                              hcd = bus_to_hcd(udev->bus);
++                              /* The xHC may think the device is already
++                               * reset, so ignore the status.
++                               */
++                              if (hcd->driver->reset_device)
++                                      hcd->driver->reset_device(hcd, udev);
++                      }
+               }
+               /* FALL THROUGH */
+       case -ENOTCONN:
+@@ -2647,7 +2652,7 @@ static void hub_port_finish_reset(struct
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               }
+-              if (!warm)
++              if (!warm && udev)
+                       usb_set_device_state(udev, *status
+                                       ? USB_STATE_NOTATTACHED
+                                       : USB_STATE_DEFAULT);
diff --git a/queue-3.8/usb-rip-out-recursive-call-on-warm-port-reset.patch b/queue-3.8/usb-rip-out-recursive-call-on-warm-port-reset.patch
new file mode 100644 (file)
index 0000000..6300ac8
--- /dev/null
@@ -0,0 +1,280 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 14:58:54 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:23:39 -0800
+Subject: USB: Rip out recursive call on warm port reset.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002339.GA13313@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit a24a6078754f28528bc91e7e7b3e6ae86bd936d8.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 65bdac5effd15d6af619b3b7218627ef4d84ed6a "USB: Handle warm
+reset failure on empty port."]
+
+When a hot reset fails on a USB 3.0 port, the current port reset code
+recursively calls hub_port_reset inside hub_port_wait_reset.  This isn't
+ideal, since we should avoid recursive calls in the kernel, and it also
+doesn't allow us to issue multiple warm resets on reset failures.
+
+Rip out the recursive call.  Instead, add code to hub_port_reset to
+issue a warm reset if the hot reset fails, and try multiple warm resets
+before giving up on the port.
+
+In hub_port_wait_reset, remove the recursive call and re-indent.  The
+code is basically the same, except:
+
+1. It bails out early if the port has transitioned to Inactive or
+Compliance Mode after the reset completed.
+
+2. It doesn't consider a connect status change to be a failed reset.  If
+multiple warm resets needed to be issued, the connect status may have
+changed, so we need to ignore that and look at the port link state
+instead.  hub_port_reset will now do that.
+
+3. It unconditionally sets udev->speed on all types of successful
+resets.  The old recursive code would set the port speed when the second
+hub_port_reset returned.
+
+The old code did not handle connected devices needing a warm reset well.
+There were only two situations that the old code handled correctly: an
+empty port needing a warm reset, and a hot reset that migrated to a warm
+reset.
+
+When an empty port needed a warm reset, hub_port_reset was called with
+the warm variable set.  The code in hub_port_finish_reset would skip
+telling the USB core and the xHC host that the device was reset, because
+otherwise that would result in a NULL pointer dereference.
+
+When a USB 3.0 device reset migrated to a warm reset, the recursive call
+made the call stack look like this:
+
+hub_port_reset(warm = false)
+        hub_wait_port_reset(warm = false)
+                hub_port_reset(warm = true)
+                        hub_wait_port_reset(warm = true)
+                        hub_port_finish_reset(warm = true)
+                        (return up the call stack to the first wait)
+
+        hub_port_finish_reset(warm = false)
+
+The old code didn't want to notify the USB core or the xHC host of device reset
+twice, so it only did it in the second call to hub_port_finish_reset,
+when warm was set to false.  This was necessary because
+before patch two ("USB: Ignore xHCI Reset Device status."), the USB core
+would pay attention to the xHC Reset Device command error status, and
+the second call would always fail.
+
+Now that we no longer have the recursive call, and warm can change from
+false to true in hub_port_reset, we need to have hub_port_finish_reset
+unconditionally notify the USB core and the xHC of the device reset.
+
+In hub_port_finish_reset, unconditionally clear the connect status
+change (CSC) bit for USB 3.0 hubs when the port reset is done.  If we
+had to issue multiple warm resets for a device, that bit may have been
+set if the device went into SS.Inactive and then was successfully warm
+reset.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |  148 ++++++++++++++++++++++---------------------------
+ 1 file changed, 67 insertions(+), 81 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2538,73 +2538,35 @@ static int hub_port_wait_reset(struct us
+               if ((portstatus & USB_PORT_STAT_RESET))
+                       goto delay;
+-              /*
+-               * Some buggy devices require a warm reset to be issued even
+-               * when the port appears not to be connected.
++              if (hub_port_warm_reset_required(hub, portstatus))
++                      return -ENOTCONN;
++
++              /* Device went away? */
++              if (!(portstatus & USB_PORT_STAT_CONNECTION))
++                      return -ENOTCONN;
++
++              /* bomb out completely if the connection bounced.  A USB 3.0
++               * connection may bounce if multiple warm resets were issued,
++               * but the device may have successfully re-connected. Ignore it.
+                */
+-              if (!warm) {
+-                      /*
+-                       * Some buggy devices can cause an NEC host controller
+-                       * to transition to the "Error" state after a hot port
+-                       * reset.  This will show up as the port state in
+-                       * "Inactive", and the port may also report a
+-                       * disconnect.  Forcing a warm port reset seems to make
+-                       * the device work.
+-                       *
+-                       * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+-                       */
+-                      if (hub_port_warm_reset_required(hub, portstatus)) {
+-                              int ret;
++              if (!hub_is_superspeed(hub->hdev) &&
++                              (portchange & USB_PORT_STAT_C_CONNECTION))
++                      return -ENOTCONN;
+-                              if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_CONNECTION);
+-                              if (portchange & USB_PORT_STAT_C_LINK_STATE)
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_PORT_LINK_STATE);
+-                              if (portchange & USB_PORT_STAT_C_RESET)
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_RESET);
+-                              dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+-                                              port1);
+-                              ret = hub_port_reset(hub, port1,
+-                                              udev, HUB_BH_RESET_TIME,
+-                                              true);
+-                              if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_CONNECTION);
+-                              return ret;
+-                      }
+-                      /* Device went away? */
+-                      if (!(portstatus & USB_PORT_STAT_CONNECTION))
+-                              return -ENOTCONN;
+-
+-                      /* bomb out completely if the connection bounced */
+-                      if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                              return -ENOTCONN;
+-
+-                      if ((portstatus & USB_PORT_STAT_ENABLE)) {
+-                              if (!udev)
+-                                      return 0;
+-
+-                              if (hub_is_wusb(hub))
+-                                      udev->speed = USB_SPEED_WIRELESS;
+-                              else if (hub_is_superspeed(hub->hdev))
+-                                      udev->speed = USB_SPEED_SUPER;
+-                              else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+-                                      udev->speed = USB_SPEED_HIGH;
+-                              else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+-                                      udev->speed = USB_SPEED_LOW;
+-                              else
+-                                      udev->speed = USB_SPEED_FULL;
++              if ((portstatus & USB_PORT_STAT_ENABLE)) {
++                      if (!udev)
+                               return 0;
+-                      }
+-              } else {
+-                      if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+-                                      hub_port_warm_reset_required(hub,
+-                                              portstatus))
+-                              return -ENOTCONN;
++                      if (hub_is_wusb(hub))
++                              udev->speed = USB_SPEED_WIRELESS;
++                      else if (hub_is_superspeed(hub->hdev))
++                              udev->speed = USB_SPEED_SUPER;
++                      else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
++                              udev->speed = USB_SPEED_HIGH;
++                      else if (portstatus & USB_PORT_STAT_LOW_SPEED)
++                              udev->speed = USB_SPEED_LOW;
++                      else
++                              udev->speed = USB_SPEED_FULL;
+                       return 0;
+               }
+@@ -2622,23 +2584,21 @@ delay:
+ }
+ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+-                      struct usb_device *udev, int *status, bool warm)
++                      struct usb_device *udev, int *status)
+ {
+       switch (*status) {
+       case 0:
+-              if (!warm) {
+-                      struct usb_hcd *hcd;
+-                      /* TRSTRCY = 10 ms; plus some extra */
+-                      msleep(10 + 40);
+-                      if (udev) {
+-                              update_devnum(udev, 0);
+-                              hcd = bus_to_hcd(udev->bus);
+-                              /* The xHC may think the device is already
+-                               * reset, so ignore the status.
+-                               */
+-                              if (hcd->driver->reset_device)
+-                                      hcd->driver->reset_device(hcd, udev);
+-                      }
++              /* TRSTRCY = 10 ms; plus some extra */
++              msleep(10 + 40);
++              if (udev) {
++                      struct usb_hcd *hcd = bus_to_hcd(udev->bus);
++
++                      update_devnum(udev, 0);
++                      /* The xHC may think the device is already reset,
++                       * so ignore the status.
++                       */
++                      if (hcd->driver->reset_device)
++                              hcd->driver->reset_device(hcd, udev);
+               }
+               /* FALL THROUGH */
+       case -ENOTCONN:
+@@ -2651,8 +2611,10 @@ static void hub_port_finish_reset(struct
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
++                      clear_port_feature(hub->hdev, port1,
++                                      USB_PORT_FEAT_C_CONNECTION);
+               }
+-              if (!warm && udev)
++              if (udev)
+                       usb_set_device_state(udev, *status
+                                       ? USB_STATE_NOTATTACHED
+                                       : USB_STATE_DEFAULT);
+@@ -2665,6 +2627,7 @@ static int hub_port_reset(struct usb_hub
+                       struct usb_device *udev, unsigned int delay, bool warm)
+ {
+       int i, status;
++      u16 portchange, portstatus;
+       if (!hub_is_superspeed(hub->hdev)) {
+               if (warm) {
+@@ -2696,10 +2659,33 @@ static int hub_port_reset(struct usb_hub
+                                               status);
+               }
+-              /* return on disconnect or reset */
++              /* Check for disconnect or reset */
+               if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+-                      hub_port_finish_reset(hub, port1, udev, &status, warm);
+-                      goto done;
++                      hub_port_finish_reset(hub, port1, udev, &status);
++
++                      if (!hub_is_superspeed(hub->hdev))
++                              goto done;
++
++                      /*
++                       * If a USB 3.0 device migrates from reset to an error
++                       * state, re-issue the warm reset.
++                       */
++                      if (hub_port_status(hub, port1,
++                                      &portstatus, &portchange) < 0)
++                              goto done;
++
++                      if (!hub_port_warm_reset_required(hub, portstatus))
++                              goto done;
++
++                      /*
++                       * If the port is in SS.Inactive or Compliance Mode, the
++                       * hot or warm reset failed.  Try another warm reset.
++                       */
++                      if (!warm) {
++                              dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
++                                              port1);
++                              warm = true;
++                      }
+               }
+               dev_dbg (hub->intfdev,