📄 xpc_main.c
字号:
/* * 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 == xpcUnloading) { /* 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);}/* * This function is called when the system is being rebooted. */static intxpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused){ enum xpc_retval reason; switch (event) { case SYS_RESTART: reason = xpcSystemReboot; break; case SYS_HALT: reason = xpcSystemHalt; break; case SYS_POWER_OFF: reason = xpcSystemPoweroff; break; default: reason = xpcSystemGoingDown; } xpc_do_exit(reason); return NOTIFY_DONE;}/* * Notify other partitions to disengage from all references to our memory. */static voidxpc_die_disengage(void){ struct xpc_partition *part; partid_t 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); } }}/* * This function is called when the system is being restarted or halted due * to some sort of system failure. If this is the case we need to notify the * other partitions to disengage from all references to our memory. * This function can also be called when our heartbeater could be offlined * for a time. In this case we need to notify other partitions to not worry * about the lack of a heartbeat. */static intxpc_system_die(struct notifier_block *nb, unsigned long event, void *unused){ switch (event) { case DIE_MACHINE_RESTART: case DIE_MACHINE_HALT: xpc_die_disengage(); break; case DIE_KDEBUG_ENTER: /* Should lack of heartbeat be ignored by other partitions? */ if (!xpc_kdebug_ignore) { break; } /* fall through */ case DIE_MCA_MONARCH_ENTER: case DIE_INIT_MONARCH_ENTER: xpc_vars->heartbeat++; xpc_vars->heartbeat_offline = 1; break; case DIE_KDEBUG_LEAVE: /* Is lack of heartbeat being ignored by other partitions? */ if (!xpc_kdebug_ignore) { break; } /* fall through */ case DIE_MCA_MONARCH_LEAVE: case DIE_INIT_MONARCH_LEAVE: xpc_vars->heartbeat++; xpc_vars->heartbeat_offline = 0; break; } return NOTIFY_DONE;}int __initxpc_init(void){ int ret; partid_t partid; struct xpc_partition *part; pid_t pid; size_t buf_size; if (!ia64_platform_is("sn2")) { return -ENODEV; } buf_size = max(XPC_RP_VARS_SIZE, XPC_RP_HEADER_SIZE + XP_NASID_MASK_BYTES); xpc_remote_copy_buffer = xpc_kmalloc_cacheline_aligned(buf_size, GFP_KERNEL, &xpc_remote_copy_buffer_base); if (xpc_remote_copy_buffer == NULL) return -ENOMEM; snprintf(xpc_part->bus_id, BUS_ID_SIZE, "part"); snprintf(xpc_chan->bus_id, BUS_ID_SIZE, "chan"); xpc_sysctl = register_sysctl_table(xpc_sys_dir); /* * The first few fields of each entry of xpc_partitions[] need to * be initialized now so that calls to xpc_connect() and * xpc_disconnect() can be made prior to the activation of any remote * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING * PARTITION HAS BEEN ACTIVATED. */ for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { part = &xpc_partitions[partid]; DBUG_ON((u64) part != L1_CACHE_ALIGN((u64) part)); part->act_IRQ_rcvd = 0; spin_lock_init(&part->act_lock); part->act_state = XPC_P_INACTIVE; XPC_SET_REASON(part, 0, 0); init_timer(&part->disengage_request_timer); part->disengage_request_timer.function = xpc_timeout_partition_disengage_request; part->disengage_request_timer.data = (unsigned long) part; part->setup_state = XPC_P_UNSET; init_waitqueue_head(&part->teardown_wq); atomic_set(&part->references, 0); } /* * Open up protections for IPI operations (and AMO operations on * Shub 1.1 systems). */ xpc_allow_IPI_ops(); /* * Interrupts being processed will increment this atomic variable and * awaken the heartbeat thread which will process the interrupts. */ atomic_set(&xpc_act_IRQ_rcvd, 0); /* * This is safe to do before the xpc_hb_checker thread has started * because the handler releases a wait queue. If an interrupt is * received before the thread is waiting, it will not go to sleep, * but rather immediately process the interrupt. */ ret = request_irq(SGI_XPC_ACTIVATE, xpc_act_IRQ_handler, 0, "xpc hb", NULL); if (ret != 0) { dev_err(xpc_part, "can't register ACTIVATE IRQ handler, " "errno=%d\n", -ret); xpc_restrict_IPI_ops(); if (xpc_sysctl) { unregister_sysctl_table(xpc_sysctl); } kfree(xpc_remote_copy_buffer_base); return -EBUSY; } /* * Fill the partition reserved page with the information needed by * other partitions to discover we are alive and establish initial * communications. */ xpc_rsvd_page = xpc_rsvd_page_init(); if (xpc_rsvd_page == NULL) { dev_err(xpc_part, "could not setup our reserved page\n"); free_irq(SGI_XPC_ACTIVATE, NULL); xpc_restrict_IPI_ops(); if (xpc_sysctl) { unregister_sysctl_table(xpc_sysctl); } kfree(xpc_remote_copy_buffer_base); return -EBUSY; } /* add ourselves to the reboot_notifier_list */ ret = register_reboot_notifier(&xpc_reboot_notifier); if (ret != 0) { dev_warn(xpc_part, "can't register reboot notifier\n"); } /* add ourselves to the die_notifier list */ ret = register_die_notifier(&xpc_die_notifier); if (ret != 0) { dev_warn(xpc_part, "can't register die notifier\n"); } init_timer(&xpc_hb_timer); xpc_hb_timer.function = xpc_hb_beater; /* * The real work-horse behind xpc. This processes incoming * interrupts and monitors remote heartbeats. */ pid = kernel_thread(xpc_hb_checker, NULL, 0); if (pid < 0) { dev_err(xpc_part, "failed while forking hb check thread\n"); /* indicate to others that our reserved page is uninitialized */ xpc_rsvd_page->vars_pa = 0; /* 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); del_timer_sync(&xpc_hb_timer); free_irq(SGI_XPC_ACTIVATE, NULL); xpc_restrict_IPI_ops(); if (xpc_sysctl) { unregister_sysctl_table(xpc_sysctl); } kfree(xpc_remote_copy_buffer_base); return -EBUSY; } /* * Startup a thread that will attempt to discover other partitions to * activate based on info provided by SAL. This new thread is short * lived and will exit once discovery is complete. */ pid = kernel_thread(xpc_initiate_discovery, NULL, 0); if (pid < 0) { dev_err(xpc_part, "failed while forking discovery thread\n"); /* mark this new thread as a non-starter */ complete(&xpc_discovery_exited); xpc_do_exit(xpcUnloading); return -EBUSY; } /* set the interface to point at XPC's functions */ xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, xpc_initiate_allocate, xpc_initiate_send, xpc_initiate_send_notify, xpc_initiate_received, xpc_initiate_partid_to_nasids); return 0;}module_init(xpc_init);void __exitxpc_exit(void){ xpc_do_exit(xpcUnloading);}module_exit(xpc_exit);MODULE_AUTHOR("Silicon Graphics, Inc.");MODULE_DESCRIPTION("Cross Partition Communication (XPC) support");MODULE_LICENSE("GPL");module_param(xpc_hb_interval, int, 0);MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " "heartbeat increments.");module_param(xpc_hb_check_interval, int, 0);MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " "heartbeat checks.");module_param(xpc_disengage_request_timelimit, int, 0);MODULE_PARM_DESC(xpc_disengage_request_timelimit, "Number of seconds to wait " "for disengage request to complete.");module_param(xpc_kdebug_ignore, int, 0);MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by " "other partitions when dropping into kdebug.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -