diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/tpm/tpm.c | 96 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 3 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 14 |
3 files changed, 61 insertions, 52 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index ae766d868454..1fee7034a386 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
@@ -954,72 +954,63 @@ EXPORT_SYMBOL_GPL(tpm_store_cancel); | |||
954 | 954 | ||
955 | /* | 955 | /* |
956 | * Device file system interface to the TPM | 956 | * Device file system interface to the TPM |
957 | * | ||
958 | * It's assured that the chip will be opened just once, | ||
959 | * by the check of is_open variable, which is protected | ||
960 | * by driver_lock. | ||
957 | */ | 961 | */ |
958 | int tpm_open(struct inode *inode, struct file *file) | 962 | int tpm_open(struct inode *inode, struct file *file) |
959 | { | 963 | { |
960 | int rc = 0, minor = iminor(inode); | 964 | int minor = iminor(inode); |
961 | struct tpm_chip *chip = NULL, *pos; | 965 | struct tpm_chip *chip = NULL, *pos; |
962 | 966 | ||
963 | lock_kernel(); | 967 | rcu_read_lock(); |
964 | spin_lock(&driver_lock); | 968 | list_for_each_entry_rcu(pos, &tpm_chip_list, list) { |
965 | |||
966 | list_for_each_entry(pos, &tpm_chip_list, list) { | ||
967 | if (pos->vendor.miscdev.minor == minor) { | 969 | if (pos->vendor.miscdev.minor == minor) { |
968 | chip = pos; | 970 | chip = pos; |
971 | get_device(chip->dev); | ||
969 | break; | 972 | break; |
970 | } | 973 | } |
971 | } | 974 | } |
975 | rcu_read_unlock(); | ||
972 | 976 | ||
973 | if (chip == NULL) { | 977 | if (!chip) |
974 | rc = -ENODEV; | 978 | return -ENODEV; |
975 | goto err_out; | ||
976 | } | ||
977 | 979 | ||
978 | if (chip->num_opens) { | 980 | if (test_and_set_bit(0, &chip->is_open)) { |
979 | dev_dbg(chip->dev, "Another process owns this TPM\n"); | 981 | dev_dbg(chip->dev, "Another process owns this TPM\n"); |
980 | rc = -EBUSY; | 982 | put_device(chip->dev); |
981 | goto err_out; | 983 | return -EBUSY; |
982 | } | 984 | } |
983 | 985 | ||
984 | chip->num_opens++; | ||
985 | get_device(chip->dev); | ||
986 | |||
987 | spin_unlock(&driver_lock); | ||
988 | |||
989 | chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); | 986 | chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); |
990 | if (chip->data_buffer == NULL) { | 987 | if (chip->data_buffer == NULL) { |
991 | chip->num_opens--; | 988 | clear_bit(0, &chip->is_open); |
992 | put_device(chip->dev); | 989 | put_device(chip->dev); |
993 | unlock_kernel(); | ||
994 | return -ENOMEM; | 990 | return -ENOMEM; |
995 | } | 991 | } |
996 | 992 | ||
997 | atomic_set(&chip->data_pending, 0); | 993 | atomic_set(&chip->data_pending, 0); |
998 | 994 | ||
999 | file->private_data = chip; | 995 | file->private_data = chip; |
1000 | unlock_kernel(); | ||
1001 | return 0; | 996 | return 0; |
1002 | |||
1003 | err_out: | ||
1004 | spin_unlock(&driver_lock); | ||
1005 | unlock_kernel(); | ||
1006 | return rc; | ||
1007 | } | 997 | } |
1008 | EXPORT_SYMBOL_GPL(tpm_open); | 998 | EXPORT_SYMBOL_GPL(tpm_open); |
1009 | 999 | ||
1000 | /* | ||
1001 | * Called on file close | ||
1002 | */ | ||
1010 | int tpm_release(struct inode *inode, struct file *file) | 1003 | int tpm_release(struct inode *inode, struct file *file) |
1011 | { | 1004 | { |
1012 | struct tpm_chip *chip = file->private_data; | 1005 | struct tpm_chip *chip = file->private_data; |
1013 | 1006 | ||
1007 | del_singleshot_timer_sync(&chip->user_read_timer); | ||
1014 | flush_scheduled_work(); | 1008 | flush_scheduled_work(); |
1015 | spin_lock(&driver_lock); | ||
1016 | file->private_data = NULL; | 1009 | file->private_data = NULL; |
1017 | del_singleshot_timer_sync(&chip->user_read_timer); | ||
1018 | atomic_set(&chip->data_pending, 0); | 1010 | atomic_set(&chip->data_pending, 0); |
1019 | chip->num_opens--; | ||
1020 | put_device(chip->dev); | ||
1021 | kfree(chip->data_buffer); | 1011 | kfree(chip->data_buffer); |
1022 | spin_unlock(&driver_lock); | 1012 | clear_bit(0, &chip->is_open); |
1013 | put_device(chip->dev); | ||
1023 | return 0; | 1014 | return 0; |
1024 | } | 1015 | } |
1025 | EXPORT_SYMBOL_GPL(tpm_release); | 1016 | EXPORT_SYMBOL_GPL(tpm_release); |
@@ -1093,13 +1084,11 @@ void tpm_remove_hardware(struct device *dev) | |||
1093 | } | 1084 | } |
1094 | 1085 | ||
1095 | spin_lock(&driver_lock); | 1086 | spin_lock(&driver_lock); |
1096 | 1087 | list_del_rcu(&chip->list); | |
1097 | list_del(&chip->list); | ||
1098 | |||
1099 | spin_unlock(&driver_lock); | 1088 | spin_unlock(&driver_lock); |
1089 | synchronize_rcu(); | ||
1100 | 1090 | ||
1101 | misc_deregister(&chip->vendor.miscdev); | 1091 | misc_deregister(&chip->vendor.miscdev); |
1102 | |||
1103 | sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); | 1092 | sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); |
1104 | tpm_bios_log_teardown(chip->bios_dir); | 1093 | tpm_bios_log_teardown(chip->bios_dir); |
1105 | 1094 | ||
@@ -1144,25 +1133,33 @@ int tpm_pm_resume(struct device *dev) | |||
1144 | } | 1133 | } |
1145 | EXPORT_SYMBOL_GPL(tpm_pm_resume); | 1134 | EXPORT_SYMBOL_GPL(tpm_pm_resume); |
1146 | 1135 | ||
1136 | /* In case vendor provided release function, call it too.*/ | ||
1137 | |||
1138 | void tpm_dev_vendor_release(struct tpm_chip *chip) | ||
1139 | { | ||
1140 | if (chip->vendor.release) | ||
1141 | chip->vendor.release(chip->dev); | ||
1142 | |||
1143 | clear_bit(chip->dev_num, dev_mask); | ||
1144 | kfree(chip->vendor.miscdev.name); | ||
1145 | } | ||
1146 | EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); | ||
1147 | |||
1148 | |||
1147 | /* | 1149 | /* |
1148 | * Once all references to platform device are down to 0, | 1150 | * Once all references to platform device are down to 0, |
1149 | * release all allocated structures. | 1151 | * release all allocated structures. |
1150 | * In case vendor provided release function, | ||
1151 | * call it too. | ||
1152 | */ | 1152 | */ |
1153 | static void tpm_dev_release(struct device *dev) | 1153 | static void tpm_dev_release(struct device *dev) |
1154 | { | 1154 | { |
1155 | struct tpm_chip *chip = dev_get_drvdata(dev); | 1155 | struct tpm_chip *chip = dev_get_drvdata(dev); |
1156 | 1156 | ||
1157 | if (chip->vendor.release) | 1157 | tpm_dev_vendor_release(chip); |
1158 | chip->vendor.release(dev); | ||
1159 | 1158 | ||
1160 | chip->release(dev); | 1159 | chip->release(dev); |
1161 | |||
1162 | clear_bit(chip->dev_num, dev_mask); | ||
1163 | kfree(chip->vendor.miscdev.name); | ||
1164 | kfree(chip); | 1160 | kfree(chip); |
1165 | } | 1161 | } |
1162 | EXPORT_SYMBOL_GPL(tpm_dev_release); | ||
1166 | 1163 | ||
1167 | /* | 1164 | /* |
1168 | * Called from tpm_<specific>.c probe function only for devices | 1165 | * Called from tpm_<specific>.c probe function only for devices |
@@ -1171,8 +1168,8 @@ static void tpm_dev_release(struct device *dev) | |||
1171 | * upon errant exit from this function specific probe function should call | 1168 | * upon errant exit from this function specific probe function should call |
1172 | * pci_disable_device | 1169 | * pci_disable_device |
1173 | */ | 1170 | */ |
1174 | struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific | 1171 | struct tpm_chip *tpm_register_hardware(struct device *dev, |
1175 | *entry) | 1172 | const struct tpm_vendor_specific *entry) |
1176 | { | 1173 | { |
1177 | #define DEVNAME_SIZE 7 | 1174 | #define DEVNAME_SIZE 7 |
1178 | 1175 | ||
@@ -1231,21 +1228,20 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend | |||
1231 | return NULL; | 1228 | return NULL; |
1232 | } | 1229 | } |
1233 | 1230 | ||
1234 | spin_lock(&driver_lock); | ||
1235 | |||
1236 | list_add(&chip->list, &tpm_chip_list); | ||
1237 | |||
1238 | spin_unlock(&driver_lock); | ||
1239 | |||
1240 | if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { | 1231 | if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { |
1241 | list_del(&chip->list); | ||
1242 | misc_deregister(&chip->vendor.miscdev); | 1232 | misc_deregister(&chip->vendor.miscdev); |
1243 | put_device(chip->dev); | 1233 | put_device(chip->dev); |
1234 | |||
1244 | return NULL; | 1235 | return NULL; |
1245 | } | 1236 | } |
1246 | 1237 | ||
1247 | chip->bios_dir = tpm_bios_log_setup(devname); | 1238 | chip->bios_dir = tpm_bios_log_setup(devname); |
1248 | 1239 | ||
1240 | /* Make chip available */ | ||
1241 | spin_lock(&driver_lock); | ||
1242 | list_add_rcu(&chip->list, &tpm_chip_list); | ||
1243 | spin_unlock(&driver_lock); | ||
1244 | |||
1249 | return chip; | 1245 | return chip; |
1250 | } | 1246 | } |
1251 | EXPORT_SYMBOL_GPL(tpm_register_hardware); | 1247 | EXPORT_SYMBOL_GPL(tpm_register_hardware); |
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index e885148b4cfb..8e30df4a4388 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h | |||
@@ -90,7 +90,7 @@ struct tpm_chip { | |||
90 | struct device *dev; /* Device stuff */ | 90 | struct device *dev; /* Device stuff */ |
91 | 91 | ||
92 | int dev_num; /* /dev/tpm# */ | 92 | int dev_num; /* /dev/tpm# */ |
93 | int num_opens; /* only one allowed */ | 93 | unsigned long is_open; /* only one allowed */ |
94 | int time_expired; | 94 | int time_expired; |
95 | 95 | ||
96 | /* Data passed to and from the tpm via the read/write calls */ | 96 | /* Data passed to and from the tpm via the read/write calls */ |
@@ -132,6 +132,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *, | |||
132 | const struct tpm_vendor_specific *); | 132 | const struct tpm_vendor_specific *); |
133 | extern int tpm_open(struct inode *, struct file *); | 133 | extern int tpm_open(struct inode *, struct file *); |
134 | extern int tpm_release(struct inode *, struct file *); | 134 | extern int tpm_release(struct inode *, struct file *); |
135 | extern void tpm_dev_vendor_release(struct tpm_chip *); | ||
135 | extern ssize_t tpm_write(struct file *, const char __user *, size_t, | 136 | extern ssize_t tpm_write(struct file *, const char __user *, size_t, |
136 | loff_t *); | 137 | loff_t *); |
137 | extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); | 138 | extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); |
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ed1879c0dd8d..717af7ad1bdf 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c | |||
@@ -630,12 +630,23 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { | |||
630 | {"", 0} /* Terminator */ | 630 | {"", 0} /* Terminator */ |
631 | }; | 631 | }; |
632 | 632 | ||
633 | static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev) | ||
634 | { | ||
635 | struct tpm_chip *chip = pnp_get_drvdata(dev); | ||
636 | |||
637 | tpm_dev_vendor_release(chip); | ||
638 | |||
639 | kfree(chip); | ||
640 | } | ||
641 | |||
642 | |||
633 | static struct pnp_driver tis_pnp_driver = { | 643 | static struct pnp_driver tis_pnp_driver = { |
634 | .name = "tpm_tis", | 644 | .name = "tpm_tis", |
635 | .id_table = tpm_pnp_tbl, | 645 | .id_table = tpm_pnp_tbl, |
636 | .probe = tpm_tis_pnp_init, | 646 | .probe = tpm_tis_pnp_init, |
637 | .suspend = tpm_tis_pnp_suspend, | 647 | .suspend = tpm_tis_pnp_suspend, |
638 | .resume = tpm_tis_pnp_resume, | 648 | .resume = tpm_tis_pnp_resume, |
649 | .remove = tpm_tis_pnp_remove, | ||
639 | }; | 650 | }; |
640 | 651 | ||
641 | #define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 | 652 | #define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 |
@@ -683,6 +694,7 @@ static void __exit cleanup_tis(void) | |||
683 | spin_lock(&tis_lock); | 694 | spin_lock(&tis_lock); |
684 | list_for_each_entry_safe(i, j, &tis_chips, list) { | 695 | list_for_each_entry_safe(i, j, &tis_chips, list) { |
685 | chip = to_tpm_chip(i); | 696 | chip = to_tpm_chip(i); |
697 | tpm_remove_hardware(chip->dev); | ||
686 | iowrite32(~TPM_GLOBAL_INT_ENABLE & | 698 | iowrite32(~TPM_GLOBAL_INT_ENABLE & |
687 | ioread32(chip->vendor.iobase + | 699 | ioread32(chip->vendor.iobase + |
688 | TPM_INT_ENABLE(chip->vendor. | 700 | TPM_INT_ENABLE(chip->vendor. |
@@ -694,9 +706,9 @@ static void __exit cleanup_tis(void) | |||
694 | free_irq(chip->vendor.irq, chip); | 706 | free_irq(chip->vendor.irq, chip); |
695 | iounmap(i->iobase); | 707 | iounmap(i->iobase); |
696 | list_del(&i->list); | 708 | list_del(&i->list); |
697 | tpm_remove_hardware(chip->dev); | ||
698 | } | 709 | } |
699 | spin_unlock(&tis_lock); | 710 | spin_unlock(&tis_lock); |
711 | |||
700 | if (force) { | 712 | if (force) { |
701 | platform_device_unregister(pdev); | 713 | platform_device_unregister(pdev); |
702 | driver_unregister(&tis_drv); | 714 | driver_unregister(&tis_drv); |