/* * Timer function to enforce the timelimit on the partition disengage. */ static void xpc_timeout_partition_disengage(unsigned long data) { struct xpc_partition *part = (struct xpc_partition *)data; DBUG_ON(time_is_after_jiffies(part->disengage_timeout)); (void)xpc_partition_disengaged(part); DBUG_ON(part->disengage_timeout != 0); DBUG_ON(xpc_partition_engaged(XPC_PARTID(part))); }
/* * Timer function to enforce the timelimit on the partition disengage request. */ static void xpc_timeout_partition_disengage_request(unsigned long data) { struct xpc_partition *part = (struct xpc_partition *)data; DBUG_ON(time_before(jiffies, part->disengage_request_timeout)); (void)xpc_partition_disengaged(part); DBUG_ON(part->disengage_request_timeout != 0); DBUG_ON(xpc_partition_engaged(1UL << XPC_PARTID(part)) != 0); }
/* * See if the other side has responded to a partition deactivate request * from us. Though we requested the remote partition to deactivate with regard * to us, we really only need to wait for the other side to disengage from us. */ int xpc_partition_disengaged(struct xpc_partition *part) { short partid = XPC_PARTID(part); int disengaged; disengaged = !xpc_partition_engaged(partid); if (part->disengage_timeout) { if (!disengaged) { if (time_is_after_jiffies(part->disengage_timeout)) { /* timelimit hasn't been reached yet */ return 0; } /* * Other side hasn't responded to our deactivate * request in a timely fashion, so assume it's dead. */ dev_info(xpc_part, "deactivate request to remote " "partition %d timed out\n", partid); xpc_disengage_timedout = 1; xpc_assume_partition_disengaged(partid); disengaged = 1; } part->disengage_timeout = 0; /* cancel the timer function, provided it's not us */ if (!in_interrupt()) del_singleshot_timer_sync(&part->disengage_timer); DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING && part->act_state != XPC_P_AS_INACTIVE); if (part->act_state != XPC_P_AS_INACTIVE) xpc_wakeup_channel_mgr(part); xpc_cancel_partition_deactivation_request(part); } return disengaged; }
/* * spin_lock_irqsave() is expected to be held on entry. */ static void xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) { struct xpc_partition *part = &xpc_partitions[ch->partid]; u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); DBUG_ON(!spin_is_locked(&ch->lock)); if (!(ch->flags & XPC_C_DISCONNECTING)) return; DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); /* make sure all activity has settled down first */ if (atomic_read(&ch->kthreads_assigned) > 0 || atomic_read(&ch->references) > 0) { return; } DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE)); if (part->act_state == XPC_P_AS_DEACTIVATING) { /* can't proceed until the other side disengages from us */ if (xpc_partition_engaged(ch->partid)) return; } else { /* as long as the other side is up do the full protocol */ if (!(ch->flags & XPC_C_RCLOSEREQUEST)) return; if (!(ch->flags & XPC_C_CLOSEREPLY)) { ch->flags |= XPC_C_CLOSEREPLY; xpc_send_chctl_closereply(ch, irq_flags); } if (!(ch->flags & XPC_C_RCLOSEREPLY)) return; } /* wake those waiting for notify completion */ if (atomic_read(&ch->n_to_notify) > 0) { /* we do callout while holding ch->lock, callout can't block */ xpc_notify_senders_of_disconnect(ch); } /* both sides are disconnected now */ if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { spin_unlock_irqrestore(&ch->lock, *irq_flags); xpc_disconnect_callout(ch, xpDisconnected); spin_lock_irqsave(&ch->lock, *irq_flags); } DBUG_ON(atomic_read(&ch->n_to_notify) != 0); /* it's now safe to free the channel's message queues */ xpc_teardown_msg_structures(ch); ch->func = NULL; ch->key = NULL; ch->entry_size = 0; ch->local_nentries = 0; ch->remote_nentries = 0; ch->kthreads_assigned_limit = 0; ch->kthreads_idle_limit = 0; /* * Mark the channel disconnected and clear all other flags, including * XPC_C_SETUP (because of call to xpc_teardown_msg_structures()) but * not including XPC_C_WDISCONNECT (if it was set). */ ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); atomic_dec(&part->nchannels_active); if (channel_was_connected) { dev_info(xpc_chan, "channel %d to partition %d disconnected, " "reason=%d\n", ch->number, ch->partid, ch->reason); } if (ch->flags & XPC_C_WDISCONNECT) { /* we won't lose the CPU since we're holding ch->lock */ complete(&ch->wdisconnect_wait); } else if (ch->delayed_chctl_flags) { if (part->act_state != XPC_P_AS_DEACTIVATING) { /* time to take action on any delayed chctl flags */ spin_lock(&part->chctl_lock); part->chctl.flags[ch->number] |= ch->delayed_chctl_flags; spin_unlock(&part->chctl_lock); } ch->delayed_chctl_flags = 0; } }
/* * Notify other partitions to deactivate from us by first disengaging from all * references to our memory. */ static void xpc_die_deactivate(void) { struct xpc_partition *part; short partid; int any_engaged; long keep_waiting; long wait_to_print; /* keep xpc_hb_checker thread from doing anything (just in case) */ xpc_exiting = 1; xpc_disallow_all_hbs(); /*indicate we're deactivated */ for (partid = 0; partid < xp_max_npartitions; partid++) { part = &xpc_partitions[partid]; if (xpc_partition_engaged(partid) || part->act_state != XPC_P_AS_INACTIVE) { xpc_request_partition_deactivation(part); xpc_indicate_partition_disengaged(part); } } /* * Though we requested that all other partitions deactivate from us, * we only wait until they've all disengaged or we've reached the * defined timelimit. * * Given that one iteration through the following while-loop takes * approximately 200 microseconds, calculate the #of loops to take * before bailing and the #of loops before printing a waiting message. */ keep_waiting = xpc_disengage_timelimit * 1000 * 5; wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; while (1) { any_engaged = xpc_any_partition_engaged(); if (!any_engaged) { dev_info(xpc_part, "all partitions have deactivated\n"); break; } if (!keep_waiting--) { for (partid = 0; partid < xp_max_npartitions; partid++) { if (xpc_partition_engaged(partid)) { dev_info(xpc_part, "deactivate from " "remote partition %d timed " "out\n", partid); } } break; } if (!wait_to_print--) { dev_info(xpc_part, "waiting for remote partitions to " "deactivate, timeout in %ld seconds\n", keep_waiting / (1000 * 5)); wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; } udelay(200); } }
static void xpc_do_exit(enum xp_retval reason) { short partid; int active_part_count, printed_waiting_msg = 0; struct xpc_partition *part; unsigned long printmsg_time, disengage_request_timeout = 0; /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */ DBUG_ON(xpc_exiting == 1); /* * Let the heartbeat checker thread and the discovery thread * (if one is running) know that they should exit. Also wake up * the heartbeat checker thread in case it's sleeping. */ xpc_exiting = 1; wake_up_interruptible(&xpc_act_IRQ_wq); /* ignore all incoming interrupts */ free_irq(SGI_XPC_ACTIVATE, NULL); /* wait for the discovery thread to exit */ wait_for_completion(&xpc_discovery_exited); /* wait for the heartbeat checker thread to exit */ wait_for_completion(&xpc_hb_checker_exited); /* sleep for a 1/3 of a second or so */ (void)msleep_interruptible(300); /* wait for all partitions to become inactive */ printmsg_time = jiffies + (XPC_DISENGAGE_PRINTMSG_INTERVAL * HZ); xpc_disengage_request_timedout = 0; do { active_part_count = 0; for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { part = &xpc_partitions[partid]; if (xpc_partition_disengaged(part) && part->act_state == XPC_P_INACTIVE) { continue; } active_part_count++; XPC_DEACTIVATE_PARTITION(part, reason); if (part->disengage_request_timeout > disengage_request_timeout) { disengage_request_timeout = part->disengage_request_timeout; } } if (xpc_partition_engaged(-1UL)) { if (time_after(jiffies, printmsg_time)) { dev_info(xpc_part, "waiting for remote " "partitions to disengage, timeout in " "%ld seconds\n", (disengage_request_timeout - jiffies) / HZ); printmsg_time = jiffies + (XPC_DISENGAGE_PRINTMSG_INTERVAL * HZ); printed_waiting_msg = 1; } } else if (active_part_count > 0) { if (printed_waiting_msg) { dev_info(xpc_part, "waiting for local partition" " to disengage\n"); printed_waiting_msg = 0; } } else { if (!xpc_disengage_request_timedout) { dev_info(xpc_part, "all partitions have " "disengaged\n"); } break; } /* sleep for a 1/3 of a second or so */ (void)msleep_interruptible(300); } while (1); DBUG_ON(xpc_partition_engaged(-1UL)); /* indicate to others that our reserved page is uninitialized */ xpc_rsvd_page->vars_pa = 0; /* now it's time to eliminate our heartbeat */ del_timer_sync(&xpc_hb_timer); DBUG_ON(xpc_vars->heartbeating_to_mask != 0); if (reason == xpUnloading) { /* take ourselves off of the reboot_notifier_list */ (void)unregister_reboot_notifier(&xpc_reboot_notifier); /* take ourselves off of the die_notifier list */ (void)unregister_die_notifier(&xpc_die_notifier); } /* close down protections for IPI operations */ xpc_restrict_IPI_ops(); /* clear the interface to XPC's functions */ xpc_clear_interface(); if (xpc_sysctl) unregister_sysctl_table(xpc_sysctl); kfree(xpc_remote_copy_buffer_base); }
/* * Notify other partitions to disengage from all references to our memory. */ static void xpc_die_disengage(void) { struct xpc_partition *part; short partid; unsigned long engaged; long time, printmsg_time, disengage_request_timeout; /* keep xpc_hb_checker thread from doing anything (just in case) */ xpc_exiting = 1; xpc_vars->heartbeating_to_mask = 0; /* indicate we're deactivated */ for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { part = &xpc_partitions[partid]; if (!XPC_SUPPORTS_DISENGAGE_REQUEST(part-> remote_vars_version)) { /* just in case it was left set by an earlier XPC */ xpc_clear_partition_engaged(1UL << partid); continue; } if (xpc_partition_engaged(1UL << partid) || part->act_state != XPC_P_INACTIVE) { xpc_request_partition_disengage(part); xpc_mark_partition_disengaged(part); xpc_IPI_send_disengage(part); } } time = rtc_time(); printmsg_time = time + (XPC_DISENGAGE_PRINTMSG_INTERVAL * sn_rtc_cycles_per_second); disengage_request_timeout = time + (xpc_disengage_request_timelimit * sn_rtc_cycles_per_second); /* wait for all other partitions to disengage from us */ while (1) { engaged = xpc_partition_engaged(-1UL); if (!engaged) { dev_info(xpc_part, "all partitions have disengaged\n"); break; } time = rtc_time(); if (time >= disengage_request_timeout) { for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { if (engaged & (1UL << partid)) { dev_info(xpc_part, "disengage from " "remote partition %d timed " "out\n", partid); } } break; } if (time >= printmsg_time) { dev_info(xpc_part, "waiting for remote partitions to " "disengage, timeout in %ld seconds\n", (disengage_request_timeout - time) / sn_rtc_cycles_per_second); printmsg_time = time + (XPC_DISENGAGE_PRINTMSG_INTERVAL * sn_rtc_cycles_per_second); } } }