diff options
author | Dmitry Kalinkin <dmitry.kalinkin@gmail.com> | 2015-09-17 19:01:44 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-04 14:50:57 -0400 |
commit | 0b0496625715374c7d0b747268c11528e8af32a3 (patch) | |
tree | 6f129e982f9f3325162afe0271414833597471e9 /drivers/vme | |
parent | 472f16f33c7d53515af83c805d4babd8a6c24a19 (diff) |
vme: change bus error handling scheme
The current VME bus error handler adds errors to the bridge error list.
vme_master_{read,write} then traverses that list to look for relevant
errors.
Such scheme didn't work well for accesses going through vme_master_mmap
because they would also allocate a vme_bus_error, but have no way to do
vme_clear_errors call to free that memory.
This changes the error handling process to be other way around: now
vme_master_{read,write} defines a window in VME address space that will
catch possible errors. VME bus error interrupt only traverses these
windows and marks those that had errors in them.
Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@gmail.com>
Cc: Igor Alekseev <igor.alekseev@itep.ru>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/vme')
-rw-r--r-- | drivers/vme/bridges/vme_ca91cx42.c | 3 | ||||
-rw-r--r-- | drivers/vme/bridges/vme_tsi148.c | 83 | ||||
-rw-r--r-- | drivers/vme/vme.c | 92 | ||||
-rw-r--r-- | drivers/vme/vme_bridge.h | 23 |
4 files changed, 91 insertions, 110 deletions
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index 1d41cf21319b..b79a74a98a23 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c | |||
@@ -204,8 +204,7 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) | |||
204 | /* Need pdev */ | 204 | /* Need pdev */ |
205 | pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); | 205 | pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); |
206 | 206 | ||
207 | /* Initialise list for VME bus errors */ | 207 | INIT_LIST_HEAD(&ca91cx42_bridge->vme_error_handlers); |
208 | INIT_LIST_HEAD(&ca91cx42_bridge->vme_errors); | ||
209 | 208 | ||
210 | mutex_init(&ca91cx42_bridge->irq_mtx); | 209 | mutex_init(&ca91cx42_bridge->irq_mtx); |
211 | 210 | ||
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 4520d985379f..d1e383bc1f17 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c | |||
@@ -314,8 +314,7 @@ static int tsi148_irq_init(struct vme_bridge *tsi148_bridge) | |||
314 | 314 | ||
315 | bridge = tsi148_bridge->driver_priv; | 315 | bridge = tsi148_bridge->driver_priv; |
316 | 316 | ||
317 | /* Initialise list for VME bus errors */ | 317 | INIT_LIST_HEAD(&tsi148_bridge->vme_error_handlers); |
318 | INIT_LIST_HEAD(&tsi148_bridge->vme_errors); | ||
319 | 318 | ||
320 | mutex_init(&tsi148_bridge->irq_mtx); | 319 | mutex_init(&tsi148_bridge->irq_mtx); |
321 | 320 | ||
@@ -1187,7 +1186,7 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, | |||
1187 | int retval, enabled; | 1186 | int retval, enabled; |
1188 | unsigned long long vme_base, size; | 1187 | unsigned long long vme_base, size; |
1189 | u32 aspace, cycle, dwidth; | 1188 | u32 aspace, cycle, dwidth; |
1190 | struct vme_bus_error *vme_err = NULL; | 1189 | struct vme_error_handler *handler; |
1191 | struct vme_bridge *tsi148_bridge; | 1190 | struct vme_bridge *tsi148_bridge; |
1192 | void __iomem *addr = image->kern_base + offset; | 1191 | void __iomem *addr = image->kern_base + offset; |
1193 | unsigned int done = 0; | 1192 | unsigned int done = 0; |
@@ -1197,6 +1196,17 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, | |||
1197 | 1196 | ||
1198 | spin_lock(&image->lock); | 1197 | spin_lock(&image->lock); |
1199 | 1198 | ||
1199 | if (err_chk) { | ||
1200 | __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, | ||
1201 | &cycle, &dwidth); | ||
1202 | handler = vme_register_error_handler(tsi148_bridge, aspace, | ||
1203 | vme_base + offset, count); | ||
1204 | if (!handler) { | ||
1205 | spin_unlock(&image->lock); | ||
1206 | return -ENOMEM; | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1200 | /* The following code handles VME address alignment. We cannot use | 1210 | /* The following code handles VME address alignment. We cannot use |
1201 | * memcpy_xxx here because it may cut data transfers in to 8-bit | 1211 | * memcpy_xxx here because it may cut data transfers in to 8-bit |
1202 | * cycles when D16 or D32 cycles are required on the VME bus. | 1212 | * cycles when D16 or D32 cycles are required on the VME bus. |
@@ -1240,24 +1250,16 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, | |||
1240 | out: | 1250 | out: |
1241 | retval = count; | 1251 | retval = count; |
1242 | 1252 | ||
1243 | if (!err_chk) | 1253 | if (err_chk) { |
1244 | goto skip_chk; | 1254 | if (handler->num_errors) { |
1245 | 1255 | dev_err(image->parent->parent, | |
1246 | __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle, | 1256 | "First VME read error detected an at address 0x%llx\n", |
1247 | &dwidth); | 1257 | handler->first_error); |
1248 | 1258 | retval = handler->first_error - (vme_base + offset); | |
1249 | vme_err = vme_find_error(tsi148_bridge, aspace, vme_base + offset, | 1259 | } |
1250 | count); | 1260 | vme_unregister_error_handler(handler); |
1251 | if (vme_err != NULL) { | ||
1252 | dev_err(image->parent->parent, "First VME read error detected " | ||
1253 | "an at address 0x%llx\n", vme_err->address); | ||
1254 | retval = vme_err->address - (vme_base + offset); | ||
1255 | /* Clear down save errors in this address range */ | ||
1256 | vme_clear_errors(tsi148_bridge, aspace, vme_base + offset, | ||
1257 | count); | ||
1258 | } | 1261 | } |
1259 | 1262 | ||
1260 | skip_chk: | ||
1261 | spin_unlock(&image->lock); | 1263 | spin_unlock(&image->lock); |
1262 | 1264 | ||
1263 | return retval; | 1265 | return retval; |
@@ -1274,7 +1276,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, | |||
1274 | unsigned int done = 0; | 1276 | unsigned int done = 0; |
1275 | unsigned int count32; | 1277 | unsigned int count32; |
1276 | 1278 | ||
1277 | struct vme_bus_error *vme_err = NULL; | 1279 | struct vme_error_handler *handler; |
1278 | struct vme_bridge *tsi148_bridge; | 1280 | struct vme_bridge *tsi148_bridge; |
1279 | struct tsi148_driver *bridge; | 1281 | struct tsi148_driver *bridge; |
1280 | 1282 | ||
@@ -1284,6 +1286,17 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, | |||
1284 | 1286 | ||
1285 | spin_lock(&image->lock); | 1287 | spin_lock(&image->lock); |
1286 | 1288 | ||
1289 | if (err_chk) { | ||
1290 | __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, | ||
1291 | &cycle, &dwidth); | ||
1292 | handler = vme_register_error_handler(tsi148_bridge, aspace, | ||
1293 | vme_base + offset, count); | ||
1294 | if (!handler) { | ||
1295 | spin_unlock(&image->lock); | ||
1296 | return -ENOMEM; | ||
1297 | } | ||
1298 | } | ||
1299 | |||
1287 | /* Here we apply for the same strategy we do in master_read | 1300 | /* Here we apply for the same strategy we do in master_read |
1288 | * function in order to assure the correct cycles. | 1301 | * function in order to assure the correct cycles. |
1289 | */ | 1302 | */ |
@@ -1333,30 +1346,18 @@ out: | |||
1333 | * We check for saved errors in the written address range/space. | 1346 | * We check for saved errors in the written address range/space. |
1334 | */ | 1347 | */ |
1335 | 1348 | ||
1336 | if (!err_chk) | 1349 | if (err_chk) { |
1337 | goto skip_chk; | 1350 | ioread16(bridge->flush_image->kern_base + 0x7F000); |
1338 | |||
1339 | /* | ||
1340 | * Get window info first, to maximise the time that the buffers may | ||
1341 | * fluch on their own | ||
1342 | */ | ||
1343 | __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle, | ||
1344 | &dwidth); | ||
1345 | |||
1346 | ioread16(bridge->flush_image->kern_base + 0x7F000); | ||
1347 | 1351 | ||
1348 | vme_err = vme_find_error(tsi148_bridge, aspace, vme_base + offset, | 1352 | if (handler->num_errors) { |
1349 | count); | 1353 | dev_warn(tsi148_bridge->parent, |
1350 | if (vme_err != NULL) { | 1354 | "First VME write error detected an at address 0x%llx\n", |
1351 | dev_warn(tsi148_bridge->parent, "First VME write error detected" | 1355 | handler->first_error); |
1352 | " an at address 0x%llx\n", vme_err->address); | 1356 | retval = handler->first_error - (vme_base + offset); |
1353 | retval = vme_err->address - (vme_base + offset); | 1357 | } |
1354 | /* Clear down save errors in this address range */ | 1358 | vme_unregister_error_handler(handler); |
1355 | vme_clear_errors(tsi148_bridge, aspace, vme_base + offset, | ||
1356 | count); | ||
1357 | } | 1359 | } |
1358 | 1360 | ||
1359 | skip_chk: | ||
1360 | spin_unlock(&image->lock); | 1361 | spin_unlock(&image->lock); |
1361 | 1362 | ||
1362 | return retval; | 1363 | return retval; |
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 2b79cd2715da..7a10d926ebb4 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c | |||
@@ -1026,76 +1026,52 @@ EXPORT_SYMBOL(vme_dma_free); | |||
1026 | void vme_bus_error_handler(struct vme_bridge *bridge, | 1026 | void vme_bus_error_handler(struct vme_bridge *bridge, |
1027 | unsigned long long address, int am) | 1027 | unsigned long long address, int am) |
1028 | { | 1028 | { |
1029 | struct vme_bus_error *error; | 1029 | struct list_head *handler_pos = NULL; |
1030 | 1030 | struct vme_error_handler *handler; | |
1031 | error = kmalloc(sizeof(struct vme_bus_error), GFP_ATOMIC); | 1031 | u32 aspace = vme_get_aspace(am); |
1032 | if (error) { | 1032 | |
1033 | error->aspace = vme_get_aspace(am); | 1033 | list_for_each(handler_pos, &bridge->vme_error_handlers) { |
1034 | error->address = address; | 1034 | handler = list_entry(handler_pos, struct vme_error_handler, |
1035 | list_add_tail(&error->list, &bridge->vme_errors); | 1035 | list); |
1036 | } else { | 1036 | if ((aspace == handler->aspace) && |
1037 | dev_err(bridge->parent, | 1037 | (address >= handler->start) && |
1038 | "Unable to alloc memory for VMEbus Error reporting\n"); | 1038 | (address < handler->end)) { |
1039 | if (!handler->num_errors) | ||
1040 | handler->first_error = address; | ||
1041 | if (handler->num_errors != UINT_MAX) | ||
1042 | handler->num_errors++; | ||
1043 | } | ||
1039 | } | 1044 | } |
1040 | } | 1045 | } |
1041 | EXPORT_SYMBOL(vme_bus_error_handler); | 1046 | EXPORT_SYMBOL(vme_bus_error_handler); |
1042 | 1047 | ||
1043 | /* | 1048 | struct vme_error_handler *vme_register_error_handler( |
1044 | * Find the first error in this address range | 1049 | struct vme_bridge *bridge, u32 aspace, |
1045 | */ | 1050 | unsigned long long address, size_t len) |
1046 | struct vme_bus_error *vme_find_error(struct vme_bridge *bridge, u32 aspace, | ||
1047 | unsigned long long address, size_t count) | ||
1048 | { | 1051 | { |
1049 | struct list_head *err_pos; | 1052 | struct vme_error_handler *handler; |
1050 | struct vme_bus_error *vme_err, *valid = NULL; | ||
1051 | unsigned long long bound; | ||
1052 | |||
1053 | bound = address + count; | ||
1054 | 1053 | ||
1055 | err_pos = NULL; | 1054 | handler = kmalloc(sizeof(*handler), GFP_KERNEL); |
1056 | /* Iterate through errors */ | 1055 | if (!handler) |
1057 | list_for_each(err_pos, &bridge->vme_errors) { | 1056 | return NULL; |
1058 | vme_err = list_entry(err_pos, struct vme_bus_error, list); | ||
1059 | if ((vme_err->aspace == aspace) && | ||
1060 | (vme_err->address >= address) && | ||
1061 | (vme_err->address < bound)) { | ||
1062 | 1057 | ||
1063 | valid = vme_err; | 1058 | handler->aspace = aspace; |
1064 | break; | 1059 | handler->start = address; |
1065 | } | 1060 | handler->end = address + len; |
1066 | } | 1061 | handler->num_errors = 0; |
1062 | handler->first_error = 0; | ||
1063 | list_add_tail(&handler->list, &bridge->vme_error_handlers); | ||
1067 | 1064 | ||
1068 | return valid; | 1065 | return handler; |
1069 | } | 1066 | } |
1070 | EXPORT_SYMBOL(vme_find_error); | 1067 | EXPORT_SYMBOL(vme_register_error_handler); |
1071 | 1068 | ||
1072 | /* | 1069 | void vme_unregister_error_handler(struct vme_error_handler *handler) |
1073 | * Clear errors in the provided address range. | ||
1074 | */ | ||
1075 | void vme_clear_errors(struct vme_bridge *bridge, u32 aspace, | ||
1076 | unsigned long long address, size_t count) | ||
1077 | { | 1070 | { |
1078 | struct list_head *err_pos, *temp; | 1071 | list_del(&handler->list); |
1079 | struct vme_bus_error *vme_err; | 1072 | kfree(handler); |
1080 | unsigned long long bound; | ||
1081 | |||
1082 | bound = address + count; | ||
1083 | |||
1084 | err_pos = NULL; | ||
1085 | /* Iterate through errors */ | ||
1086 | list_for_each_safe(err_pos, temp, &bridge->vme_errors) { | ||
1087 | vme_err = list_entry(err_pos, struct vme_bus_error, list); | ||
1088 | |||
1089 | if ((vme_err->aspace == aspace) && | ||
1090 | (vme_err->address >= address) && | ||
1091 | (vme_err->address < bound)) { | ||
1092 | |||
1093 | list_del(err_pos); | ||
1094 | kfree(vme_err); | ||
1095 | } | ||
1096 | } | ||
1097 | } | 1073 | } |
1098 | EXPORT_SYMBOL(vme_clear_errors); | 1074 | EXPORT_SYMBOL(vme_unregister_error_handler); |
1099 | 1075 | ||
1100 | void vme_irq_handler(struct vme_bridge *bridge, int level, int statid) | 1076 | void vme_irq_handler(struct vme_bridge *bridge, int level, int statid) |
1101 | { | 1077 | { |
diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h index 92fbe18cbc42..397578a73883 100644 --- a/drivers/vme/vme_bridge.h +++ b/drivers/vme/vme_bridge.h | |||
@@ -75,10 +75,13 @@ struct vme_lm_resource { | |||
75 | int monitors; | 75 | int monitors; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | struct vme_bus_error { | 78 | struct vme_error_handler { |
79 | struct list_head list; | 79 | struct list_head list; |
80 | u32 aspace; | 80 | unsigned long long start; /* Beginning of error window */ |
81 | unsigned long long address; | 81 | unsigned long long end; /* End of error window */ |
82 | unsigned long long first_error; /* Address of the first error */ | ||
83 | u32 aspace; /* Address space of error window*/ | ||
84 | unsigned num_errors; /* Number of errors */ | ||
82 | }; | 85 | }; |
83 | 86 | ||
84 | struct vme_callback { | 87 | struct vme_callback { |
@@ -106,8 +109,10 @@ struct vme_bridge { | |||
106 | struct list_head dma_resources; | 109 | struct list_head dma_resources; |
107 | struct list_head lm_resources; | 110 | struct list_head lm_resources; |
108 | 111 | ||
109 | struct list_head vme_errors; /* List for errors generated on VME */ | 112 | /* List for registered errors handlers */ |
110 | struct list_head devices; /* List of devices on this bridge */ | 113 | struct list_head vme_error_handlers; |
114 | /* List of devices on this bridge */ | ||
115 | struct list_head devices; | ||
111 | 116 | ||
112 | /* Bridge Info - XXX Move to private structure? */ | 117 | /* Bridge Info - XXX Move to private structure? */ |
113 | struct device *parent; /* Parent device (eg. pdev->dev for PCI) */ | 118 | struct device *parent; /* Parent device (eg. pdev->dev for PCI) */ |
@@ -168,13 +173,13 @@ struct vme_bridge { | |||
168 | 173 | ||
169 | void vme_bus_error_handler(struct vme_bridge *bridge, | 174 | void vme_bus_error_handler(struct vme_bridge *bridge, |
170 | unsigned long long address, int am); | 175 | unsigned long long address, int am); |
171 | struct vme_bus_error *vme_find_error(struct vme_bridge *bridge, u32 aspace, | ||
172 | unsigned long long address, size_t count); | ||
173 | void vme_clear_errors(struct vme_bridge *bridge, u32 aspace, | ||
174 | unsigned long long address, size_t count); | ||
175 | void vme_irq_handler(struct vme_bridge *, int, int); | 176 | void vme_irq_handler(struct vme_bridge *, int, int); |
176 | 177 | ||
177 | int vme_register_bridge(struct vme_bridge *); | 178 | int vme_register_bridge(struct vme_bridge *); |
178 | void vme_unregister_bridge(struct vme_bridge *); | 179 | void vme_unregister_bridge(struct vme_bridge *); |
180 | struct vme_error_handler *vme_register_error_handler( | ||
181 | struct vme_bridge *bridge, u32 aspace, | ||
182 | unsigned long long address, size_t len); | ||
183 | void vme_unregister_error_handler(struct vme_error_handler *handler); | ||
179 | 184 | ||
180 | #endif /* _VME_BRIDGE_H_ */ | 185 | #endif /* _VME_BRIDGE_H_ */ |