===================================================================
@@ -98,17 +98,56 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem
return td;
}
+static void td_check(struct ohci_hcd *hc, int hash, char *msg)
+{
+ struct td *td, *first;
+
+ first = hc->td_hash[hash];
+ for (td = first; td; td = td->td_hash) {
+ if (td->td_hash == first || td->td_hash == td) {
+ ohci_err(hc, "Circular pointer %s: %d %p %p %p\n",
+ msg, hash, first, td, td->td_hash);
+ td->td_hash = NULL;
+ return;
+ }
+ }
+}
+
+static void td_check_all(struct ohci_hcd *hc, char *msg)
+{
+ int hash;
+
+ for (hash = 0; hash < TD_HASH_SIZE; ++hash)
+ td_check(hc, hash, msg);
+}
+
static void
td_free (struct ohci_hcd *hc, struct td *td)
{
- struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
+ int hash = TD_HASH_FUNC(td->td_dma);
+ struct td **prev = &hc->td_hash[hash];
- while (*prev && *prev != td)
+ td_check(hc, hash, "#1a");
+ while (*prev && *prev != td) {
+ if ((unsigned long) *prev == 0xa7a7a7a7) {
+ ohci_err(hc, "poisoned hash at %p (%d) %p %p\n", prev,
+ hash, td, hc->td_hash[hash]);
+ return;
+ }
prev = &(*prev)->td_hash;
- if (*prev)
+ }
+ if (*prev) {
*prev = td->td_hash;
+ if (*prev == td) {
+ ohci_err(hc, "invalid hash at %p (%d) %p %p\n", prev,
+ hash, td, hc->td_hash[hash]);
+ *prev = NULL;
+ }
+ }
else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
ohci_dbg (hc, "no hash for td %p\n", td);
+ mb();
+ td_check(hc, hash, "#1b");
dma_pool_free (hc->td_cache, td, td->td_dma);
}
===================================================================
@@ -558,12 +558,14 @@ td_fill (struct ohci_hcd *ohci, u32 info
/* hash it for later reverse mapping */
hash = TD_HASH_FUNC (td->td_dma);
+ td_check(ohci, hash, "#2a");
td->td_hash = ohci->td_hash [hash];
ohci->td_hash [hash] = td;
/* HC might read the TD (or cachelines) right away ... */
wmb ();
td->ed->hwTailP = td->hwNextTD;
+ td_check(ohci, hash, "#2b");
}
/*-------------------------------------------------------------------------*/
@@ -1127,9 +1129,11 @@ dl_done_list (struct ohci_hcd *ohci)
{
struct td *td = dl_reverse_done_list (ohci);
+ td_check_all(ohci, "#3a");
while (td) {
struct td *td_next = td->next_dl_td;
takeback_td(ohci, td);
td = td_next;
}
+ td_check_all(ohci, "#3b");
}