Fix bugs related to module unload of the GRU driver.
Signed-off-by: Jack Steiner <steiner@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
 
 static void __exit gru_exit(void)
 {
-       int i, bid;
+       int i, bid, gid;
        int order = get_order(sizeof(struct gru_state) *
                              GRU_CHIPLETS_PER_BLADE);
 
        for (i = 0; i < GRU_CHIPLETS_PER_BLADE; i++)
                free_irq(IRQ_GRU + i, NULL);
 
+       foreach_gid(gid)
+               gru_kservices_exit(GID_TO_GRU(gid));
+
        for (bid = 0; bid < GRU_MAX_BLADES; bid++)
                free_pages((unsigned long)gru_base[bid], order);
 
 
                quicktest(gru);
        return 0;
 }
+
+void gru_kservices_exit(struct gru_state *gru)
+{
+       struct gru_context_configuration_handle *cch;
+       struct gru_blade_state *bs;
+
+       bs = gru->gs_blade;
+       if (gru != &bs->bs_grus[1])
+               return;
+
+       cch = get_cch(gru->gs_gru_base_vaddr, KERNEL_CTXNUM);
+       lock_cch_handle(cch);
+       if (cch_interrupt_sync(cch))
+               BUG();
+       if (cch_deallocate(cch))
+               BUG();
+       unlock_cch_handle(cch);
+}
+
 
 extern void gts_drop(struct gru_thread_state *gts);
 extern void gru_tgh_flush_init(struct gru_state *gru);
 extern int gru_kservices_init(struct gru_state *gru);
+extern void gru_kservices_exit(struct gru_state *gru);
 extern irqreturn_t gru_intr(int irq, void *dev_id);
 extern int gru_handle_user_call_os(unsigned long address);
 extern int gru_user_flush_tlb(unsigned long arg);