runtime: Restore scheduler stack position if cgo callback panics.

If we don't do this, then when C code calls back to Go code
which panics, we lose space on the scheduler stack.  If that
happens a lot, eventually there is no space left on the
scheduler stack.

R=rsc
CC=golang-dev
https://golang.org/cl/3898042
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index d30d598..d469e7c 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -619,26 +619,59 @@
 	runtime·gosched();
 }
 
+// Restore the position of m's scheduler stack if we unwind the stack
+// through a cgo callback.
+static void
+runtime·unwindcgocallback(void **spaddr, void *sp)
+{
+	*spaddr = sp;
+}
+
 // Start scheduling g1 again for a cgo callback.
 void
 runtime·startcgocallback(G* g1)
 {
+	Defer *d;
+	uintptr arg;
+
 	runtime·lock(&runtime·sched);
 	g1->status = Grunning;
 	runtime·sched.msyscall--;
 	runtime·sched.mcpu++;
 	runtime·unlock(&runtime·sched);
+
+	// Add an entry to the defer stack which restores the old
+	// position of m's scheduler stack.  This is so that if the
+	// code we are calling panics, we won't lose the space on the
+	// scheduler stack.  Note that we are locked to this m here.
+	d = runtime·malloc(sizeof(*d) + 2*sizeof(void*) - sizeof(d->args));
+	d->fn = (byte*)runtime·unwindcgocallback;
+	d->siz = 2 * sizeof(uintptr);
+	((void**)d->args)[0] = &m->sched.sp;
+	((void**)d->args)[1] = m->sched.sp;
+	d->link = g1->defer;
+	g1->defer = d;
 }
 
 // Stop scheduling g1 after a cgo callback.
 void
 runtime·endcgocallback(G* g1)
 {
+	Defer *d;
+
 	runtime·lock(&runtime·sched);
 	g1->status = Gsyscall;
 	runtime·sched.mcpu--;
 	runtime·sched.msyscall++;
 	runtime·unlock(&runtime·sched);
+
+	// Remove the entry on the defer stack added by
+	// startcgocallback.
+	d = g1->defer;
+	if (d == nil || d->fn != (byte*)runtime·unwindcgocallback)
+		runtime·throw("bad defer entry in endcgocallback");
+	g1->defer = d->link;
+	runtime·free(d);
 }
 
 /*