aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard MUSIL <richard.musil@st.com>2008-02-06 04:37:02 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:04 -0500
commit5bd91f18be2fc0dd0384fbfca6d3cdd79a8050dd (patch)
treefe34a0db0cf9ee8194fda3c778a44d67e6c4b0a4
parenteed4a2aba7ff6d8c40d3d55b81f80352765ffcee (diff)
tpm.c: fix crash during device removal
The clean up procedure now uses platform device "release" callback to handle memory clean up. For this purpose "release" function callback was added to struct tpm_vendor_specific, so hw device driver provider can get called when it is safe to remove all allocated resources. This is supposed to fix a bug in device removal, where device while in receive function (waiting on timeout) was prone to segfault, if the tpm_chip struct was unallocated before the timeout expired (in tpm_remove_hardware). Acked-by: Marcel Selhorst <tpm@selhorst.net> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/char/tpm/tpm.c44
-rw-r--r--drivers/char/tpm/tpm.h2
2 files changed, 29 insertions, 17 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index c88424a0c89b..a5d8bcb40000 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -1031,18 +1031,13 @@ void tpm_remove_hardware(struct device *dev)
1031 1031
1032 spin_unlock(&driver_lock); 1032 spin_unlock(&driver_lock);
1033 1033
1034 dev_set_drvdata(dev, NULL);
1035 misc_deregister(&chip->vendor.miscdev); 1034 misc_deregister(&chip->vendor.miscdev);
1036 kfree(chip->vendor.miscdev.name);
1037 1035
1038 sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); 1036 sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
1039 tpm_bios_log_teardown(chip->bios_dir); 1037 tpm_bios_log_teardown(chip->bios_dir);
1040 1038
1041 clear_bit(chip->dev_num, dev_mask); 1039 /* write it this way to be explicit (chip->dev == dev) */
1042 1040 put_device(chip->dev);
1043 kfree(chip);
1044
1045 put_device(dev);
1046} 1041}
1047EXPORT_SYMBOL_GPL(tpm_remove_hardware); 1042EXPORT_SYMBOL_GPL(tpm_remove_hardware);
1048 1043
@@ -1083,6 +1078,26 @@ int tpm_pm_resume(struct device *dev)
1083EXPORT_SYMBOL_GPL(tpm_pm_resume); 1078EXPORT_SYMBOL_GPL(tpm_pm_resume);
1084 1079
1085/* 1080/*
1081 * Once all references to platform device are down to 0,
1082 * release all allocated structures.
1083 * In case vendor provided release function,
1084 * call it too.
1085 */
1086static void tpm_dev_release(struct device *dev)
1087{
1088 struct tpm_chip *chip = dev_get_drvdata(dev);
1089
1090 if (chip->vendor.release)
1091 chip->vendor.release(dev);
1092
1093 chip->release(dev);
1094
1095 clear_bit(chip->dev_num, dev_mask);
1096 kfree(chip->vendor.miscdev.name);
1097 kfree(chip);
1098}
1099
1100/*
1086 * Called from tpm_<specific>.c probe function only for devices 1101 * Called from tpm_<specific>.c probe function only for devices
1087 * the driver has determined it should claim. Prior to calling 1102 * the driver has determined it should claim. Prior to calling
1088 * this function the specific probe function has called pci_enable_device 1103 * this function the specific probe function has called pci_enable_device
@@ -1136,23 +1151,21 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
1136 1151
1137 chip->vendor.miscdev.parent = dev; 1152 chip->vendor.miscdev.parent = dev;
1138 chip->dev = get_device(dev); 1153 chip->dev = get_device(dev);
1154 chip->release = dev->release;
1155 dev->release = tpm_dev_release;
1156 dev_set_drvdata(dev, chip);
1139 1157
1140 if (misc_register(&chip->vendor.miscdev)) { 1158 if (misc_register(&chip->vendor.miscdev)) {
1141 dev_err(chip->dev, 1159 dev_err(chip->dev,
1142 "unable to misc_register %s, minor %d\n", 1160 "unable to misc_register %s, minor %d\n",
1143 chip->vendor.miscdev.name, 1161 chip->vendor.miscdev.name,
1144 chip->vendor.miscdev.minor); 1162 chip->vendor.miscdev.minor);
1145 put_device(dev); 1163 put_device(chip->dev);
1146 clear_bit(chip->dev_num, dev_mask);
1147 kfree(chip);
1148 kfree(devname);
1149 return NULL; 1164 return NULL;
1150 } 1165 }
1151 1166
1152 spin_lock(&driver_lock); 1167 spin_lock(&driver_lock);
1153 1168
1154 dev_set_drvdata(dev, chip);
1155
1156 list_add(&chip->list, &tpm_chip_list); 1169 list_add(&chip->list, &tpm_chip_list);
1157 1170
1158 spin_unlock(&driver_lock); 1171 spin_unlock(&driver_lock);
@@ -1160,10 +1173,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
1160 if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { 1173 if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
1161 list_del(&chip->list); 1174 list_del(&chip->list);
1162 misc_deregister(&chip->vendor.miscdev); 1175 misc_deregister(&chip->vendor.miscdev);
1163 put_device(dev); 1176 put_device(chip->dev);
1164 clear_bit(chip->dev_num, dev_mask);
1165 kfree(chip);
1166 kfree(devname);
1167 return NULL; 1177 return NULL;
1168 } 1178 }
1169 1179
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index d15ccddc92eb..e885148b4cfb 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -74,6 +74,7 @@ struct tpm_vendor_specific {
74 int (*send) (struct tpm_chip *, u8 *, size_t); 74 int (*send) (struct tpm_chip *, u8 *, size_t);
75 void (*cancel) (struct tpm_chip *); 75 void (*cancel) (struct tpm_chip *);
76 u8 (*status) (struct tpm_chip *); 76 u8 (*status) (struct tpm_chip *);
77 void (*release) (struct device *);
77 struct miscdevice miscdev; 78 struct miscdevice miscdev;
78 struct attribute_group *attr_group; 79 struct attribute_group *attr_group;
79 struct list_head list; 80 struct list_head list;
@@ -106,6 +107,7 @@ struct tpm_chip {
106 struct dentry **bios_dir; 107 struct dentry **bios_dir;
107 108
108 struct list_head list; 109 struct list_head list;
110 void (*release) (struct device *);
109}; 111};
110 112
111#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) 113#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)