Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dca: redesign locks to fix deadlocks

Change spin_locks to irqsave to prevent dead-locks.
Protect adding and deleting to/from dca_providers list.
Drop the lock during dca_sysfs_add_req() and dca_sysfs_remove_req() calls
as they might sleep (use GFP_KERNEL allocation).

Signed-off-by: Maciej Sosnowski <maciej.sosnowski@intel.com>
Acked-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Maciej Sosnowski and committed by
David S. Miller
eb4400e3 ff01b916

+33 -18
+33 -18
drivers/dca/dca-core.c
··· 28 28 #include <linux/device.h> 29 29 #include <linux/dca.h> 30 30 31 - #define DCA_VERSION "1.4" 31 + #define DCA_VERSION "1.8" 32 32 33 33 MODULE_VERSION(DCA_VERSION); 34 34 MODULE_LICENSE("GPL"); ··· 60 60 { 61 61 struct dca_provider *dca; 62 62 int err, slot = -ENODEV; 63 + unsigned long flags; 63 64 64 65 if (!dev) 65 66 return -EFAULT; 66 67 67 - spin_lock(&dca_lock); 68 + spin_lock_irqsave(&dca_lock, flags); 68 69 69 70 /* check if the requester has not been added already */ 70 71 dca = dca_find_provider_by_dev(dev); 71 72 if (dca) { 72 - spin_unlock(&dca_lock); 73 + spin_unlock_irqrestore(&dca_lock, flags); 73 74 return -EEXIST; 74 75 } 75 76 ··· 79 78 if (slot >= 0) 80 79 break; 81 80 } 82 - if (slot < 0) { 83 - spin_unlock(&dca_lock); 81 + 82 + spin_unlock_irqrestore(&dca_lock, flags); 83 + 84 + if (slot < 0) 84 85 return slot; 85 - } 86 86 87 87 err = dca_sysfs_add_req(dca, dev, slot); 88 88 if (err) { 89 - dca->ops->remove_requester(dca, dev); 90 - spin_unlock(&dca_lock); 89 + spin_lock_irqsave(&dca_lock, flags); 90 + if (dca == dca_find_provider_by_dev(dev)) 91 + dca->ops->remove_requester(dca, dev); 92 + spin_unlock_irqrestore(&dca_lock, flags); 91 93 return err; 92 94 } 93 95 94 - spin_unlock(&dca_lock); 95 96 return 0; 96 97 } 97 98 EXPORT_SYMBOL_GPL(dca_add_requester); ··· 106 103 { 107 104 struct dca_provider *dca; 108 105 int slot; 106 + unsigned long flags; 109 107 110 108 if (!dev) 111 109 return -EFAULT; 112 110 113 - spin_lock(&dca_lock); 111 + spin_lock_irqsave(&dca_lock, flags); 114 112 dca = dca_find_provider_by_dev(dev); 115 113 if (!dca) { 116 - spin_unlock(&dca_lock); 114 + spin_unlock_irqrestore(&dca_lock, flags); 117 115 return -ENODEV; 118 116 } 119 117 slot = dca->ops->remove_requester(dca, dev); 120 - if (slot < 0) { 121 - spin_unlock(&dca_lock); 118 + spin_unlock_irqrestore(&dca_lock, flags); 119 + 120 + if (slot < 0) 122 121 return slot; 123 - } 124 122 125 123 dca_sysfs_remove_req(dca, slot); 126 124 127 - spin_unlock(&dca_lock); 128 125 return 0; 129 126 } 130 127 EXPORT_SYMBOL_GPL(dca_remove_requester); ··· 138 135 { 139 136 struct dca_provider *dca; 140 137 u8 tag; 138 + unsigned long flags; 141 139 142 - spin_lock(&dca_lock); 140 + spin_lock_irqsave(&dca_lock, flags); 143 141 144 142 dca = dca_find_provider_by_dev(dev); 145 143 if (!dca) { 146 - spin_unlock(&dca_lock); 144 + spin_unlock_irqrestore(&dca_lock, flags); 147 145 return -ENODEV; 148 146 } 149 147 tag = dca->ops->get_tag(dca, dev, cpu); 150 148 151 - spin_unlock(&dca_lock); 149 + spin_unlock_irqrestore(&dca_lock, flags); 152 150 return tag; 153 151 } 154 152 ··· 221 217 int register_dca_provider(struct dca_provider *dca, struct device *dev) 222 218 { 223 219 int err; 220 + unsigned long flags; 224 221 225 222 err = dca_sysfs_add_provider(dca, dev); 226 223 if (err) 227 224 return err; 225 + 226 + spin_lock_irqsave(&dca_lock, flags); 228 227 list_add(&dca->node, &dca_providers); 228 + spin_unlock_irqrestore(&dca_lock, flags); 229 + 229 230 blocking_notifier_call_chain(&dca_provider_chain, 230 231 DCA_PROVIDER_ADD, NULL); 231 232 return 0; ··· 243 234 */ 244 235 void unregister_dca_provider(struct dca_provider *dca) 245 236 { 237 + unsigned long flags; 238 + 246 239 blocking_notifier_call_chain(&dca_provider_chain, 247 240 DCA_PROVIDER_REMOVE, NULL); 241 + 242 + spin_lock_irqsave(&dca_lock, flags); 248 243 list_del(&dca->node); 244 + spin_unlock_irqrestore(&dca_lock, flags); 245 + 249 246 dca_sysfs_remove_provider(dca); 250 247 } 251 248 EXPORT_SYMBOL_GPL(unregister_dca_provider);