x86: add deregister_irq()

It's about as good as register_irq().

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/riscv/trap.c b/kern/arch/riscv/trap.c
index d3be282..78150be 100644
--- a/kern/arch/riscv/trap.c
+++ b/kern/arch/riscv/trap.c
@@ -350,6 +350,12 @@
 	return -1;
 }
 
+int deregister_irq(int vector, uint32_t tbdf)
+{
+	printk("%s not implemented\n", __FUNCTION);
+	return -1;
+}
+
 int route_irqs(int cpu_vec, int coreid)
 {
 	printk("%s not implemented\n", __FUNCTION);
diff --git a/kern/arch/x86/trap.c b/kern/arch/x86/trap.c
index 16cdc5c..799d6e3 100644
--- a/kern/arch/x86/trap.c
+++ b/kern/arch/x86/trap.c
@@ -815,6 +815,29 @@
 	return irq_h;
 }
 
+int deregister_irq(int vector, uint32_t tbdf)
+{
+	struct irq_handler *irq_h, **pp;
+
+	pp = &irq_handlers[vector];
+	spin_lock_irqsave(&irq_handler_wlock);
+	while ((irq_h = *pp)) {
+		if (irq_h->tbdf == tbdf) {
+			rcu_assign_pointer(*pp, irq_h->next);
+			break;
+		}
+		pp = &irq_h->next;
+	}
+	spin_unlock_irqsave(&irq_handler_wlock);
+	if (!irq_h) {
+		warn("No IRQ V: %d TBDF: %x to unregister!", vector, tbdf);
+		return -1;
+	}
+	synchronize_rcu();
+	kfree(irq_h);
+	return 0;
+}
+
 /* These routing functions only allow the routing of an irq to a single core.
  * If we want to route to multiple cores, we'll probably need to set up logical
  * groups or something and take some additional parameters. */
diff --git a/kern/include/trap.h b/kern/include/trap.h
index 66d0d1b..c3ae8d1 100644
--- a/kern/include/trap.h
+++ b/kern/include/trap.h
@@ -16,6 +16,7 @@
 void idt_init(void);
 struct irq_handler *register_irq(int irq, isr_t handler, void *irq_arg,
 				 uint32_t tbdf);
+int deregister_irq(int vector, uint32_t tbdf);
 int route_irqs(int cpu_vec, int coreid);
 void print_trapframe(struct hw_trapframe *hw_tf);
 void print_swtrapframe(struct sw_trapframe *sw_tf);