+kpr_do_upcall (void *arg)
+{
+ kpr_upcall_t *u = (kpr_upcall_t *)arg;
+ char nalstr[10];
+ char nidstr[36];
+ char whenstr[36];
+ char *argv[] = {
+ NULL,
+ "ROUTER_NOTIFY",
+ nalstr,
+ nidstr,
+ u->kpru_alive ? "up" : "down",
+ whenstr,
+ NULL};
+
+ snprintf (nalstr, sizeof(nalstr), "%d", u->kpru_nal_id);
+ snprintf (nidstr, sizeof(nidstr), LPX64, u->kpru_nid);
+ snprintf (whenstr, sizeof(whenstr), "%ld", u->kpru_when);
+
+ portals_run_upcall (argv);
+
+ kfree (u);
+}
+
+void
+kpr_upcall (int gw_nalid, ptl_nid_t gw_nid, int alive, time_t when)
+{
+ char str[PTL_NALFMT_SIZE];
+
+ /* May be in arbitrary context */
+ kpr_upcall_t *u = kmalloc (sizeof (kpr_upcall_t), GFP_ATOMIC);
+
+ if (u == NULL) {
+ CERROR ("Upcall out of memory: nal %x nid "LPX64" (%s) %s\n",
+ gw_nalid, gw_nid,
+ portals_nid2str(gw_nalid, gw_nid, str),
+ alive ? "up" : "down");
+ return;
+ }
+
+ u->kpru_nal_id = gw_nalid;
+ u->kpru_nid = gw_nid;
+ u->kpru_alive = alive;
+ u->kpru_when = when;
+
+ prepare_work (&u->kpru_tq, kpr_do_upcall, u);
+ schedule_work (&u->kpru_tq);
+}
+
+int
+kpr_do_notify (int byNal, int gateway_nalid, ptl_nid_t gateway_nid,
+ int alive, time_t when)
+{
+ unsigned long flags;
+ int found;
+ kpr_nal_entry_t *ne = NULL;
+ kpr_gateway_entry_t *ge = NULL;
+ struct timeval now;
+ struct list_head *e;
+ struct list_head *n;
+ char str[PTL_NALFMT_SIZE];
+
+ CDEBUG (D_NET, "%s notifying [%x] "LPX64": %s\n",
+ byNal ? "NAL" : "userspace",
+ gateway_nalid, gateway_nid, alive ? "up" : "down");
+
+ /* can't do predictions... */
+ do_gettimeofday (&now);
+ if (when > now.tv_sec) {
+ CWARN ("Ignoring prediction from %s of [%x] "LPX64" %s "
+ "%ld seconds in the future\n",
+ byNal ? "NAL" : "userspace",
+ gateway_nalid, gateway_nid,
+ alive ? "up" : "down",
+ when - now.tv_sec);
+ return (EINVAL);
+ }
+
+ LASSERT (when <= now.tv_sec);
+
+ /* Serialise with lookups (i.e. write lock) */
+ write_lock_irqsave(&kpr_rwlock, flags);
+
+ found = 0;
+ list_for_each_safe (e, n, &kpr_gateways) {
+
+ ge = list_entry(e, kpr_gateway_entry_t, kpge_list);
+ if ((gateway_nalid != 0 &&
+ ge->kpge_nalid != gateway_nalid) ||
+ ge->kpge_nid != gateway_nid)
+ continue;
+
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ /* gateway not found */
+ write_unlock_irqrestore(&kpr_rwlock, flags);
+ CDEBUG (D_NET, "Gateway not found\n");
+ return (0);
+ }
+
+ if (when < ge->kpge_timestamp) {
+ /* out of date information */
+ write_unlock_irqrestore (&kpr_rwlock, flags);
+ CDEBUG (D_NET, "Out of date\n");
+ return (0);
+ }
+
+ /* update timestamp */
+ ge->kpge_timestamp = when;
+
+ if ((!ge->kpge_alive) == (!alive)) {
+ /* new date for old news */
+ write_unlock_irqrestore (&kpr_rwlock, flags);
+ CDEBUG (D_NET, "Old news\n");
+ return (0);
+ }
+
+ ge->kpge_alive = alive;
+ CDEBUG(D_NET, "set "LPX64" [%p] %d\n", gateway_nid, ge, alive);
+
+ if (alive) {
+ /* Reset all gateway weights so the newly-enabled gateway
+ * doesn't have to play catch-up */
+ list_for_each_safe (e, n, &kpr_gateways) {
+ kpr_gateway_entry_t *ge = list_entry(e, kpr_gateway_entry_t,
+ kpge_list);
+ atomic_set (&ge->kpge_weight, 0);
+ }
+ }
+
+ found = 0;
+ if (!byNal) {
+ /* userland notified me: notify NAL? */
+ ne = kpr_find_nal_entry_locked (ge->kpge_nalid);
+ if (ne != NULL) {
+ if (!ne->kpne_shutdown &&
+ ne->kpne_interface.kprni_notify != NULL) {
+ /* take a ref on this NAL until notifying
+ * it has completed... */
+ atomic_inc (&ne->kpne_refcount);
+ found = 1;
+ }
+ }
+ }
+
+ write_unlock_irqrestore(&kpr_rwlock, flags);
+
+ if (found) {
+ ne->kpne_interface.kprni_notify (ne->kpne_interface.kprni_arg,
+ gateway_nid, alive);
+ /* 'ne' can disappear now... */
+ atomic_dec (&ne->kpne_refcount);
+ }
+
+ if (byNal) {
+ /* It wasn't userland that notified me... */
+ CWARN ("Upcall: NAL %x NID "LPX64" (%s) is %s\n",
+ gateway_nalid, gateway_nid,
+ portals_nid2str(gateway_nalid, gateway_nid, str),
+ alive ? "alive" : "dead");
+ kpr_upcall (gateway_nalid, gateway_nid, alive, when);
+ } else {
+ CDEBUG (D_NET, " NOT Doing upcall\n");
+ }
+
+ return (0);
+}
+
+void
+kpr_nal_notify (void *arg, ptl_nid_t peer, int alive, time_t when)
+{
+ kpr_nal_entry_t *ne = (kpr_nal_entry_t *)arg;
+
+ kpr_do_notify (1, ne->kpne_interface.kprni_nalid, peer, alive, when);
+}
+
+void