162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP 762306a36Sopenharmony_ci * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Cross Partition Communication (XPC) support - standard version. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * XPC provides a message passing capability that crosses partition 1462306a36Sopenharmony_ci * boundaries. This module is made up of two parts: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * partition This part detects the presence/absence of other 1762306a36Sopenharmony_ci * partitions. It provides a heartbeat and monitors 1862306a36Sopenharmony_ci * the heartbeats of other partitions. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * channel This part manages the channels and sends/receives 2162306a36Sopenharmony_ci * messages across them to/from other partitions. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * There are a couple of additional functions residing in XP, which 2462306a36Sopenharmony_ci * provide an interface to XPC for its users. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Caveats: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * . Currently on sn2, we have no way to determine which nasid an IRQ 3062306a36Sopenharmony_ci * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write 3162306a36Sopenharmony_ci * followed by an IPI. The amo indicates where data is to be pulled 3262306a36Sopenharmony_ci * from, so after the IPI arrives, the remote partition checks the amo 3362306a36Sopenharmony_ci * word. The IPI can actually arrive before the amo however, so other 3462306a36Sopenharmony_ci * code must periodically check for this case. Also, remote amo 3562306a36Sopenharmony_ci * operations do not reliably time out. Thus we do a remote PIO read 3662306a36Sopenharmony_ci * solely to know whether the remote partition is down and whether we 3762306a36Sopenharmony_ci * should stop sending IPIs to it. This remote PIO read operation is 3862306a36Sopenharmony_ci * set up in a special nofault region so SAL knows to ignore (and 3962306a36Sopenharmony_ci * cleanup) any errors due to the remote amo write, PIO read, and/or 4062306a36Sopenharmony_ci * PIO write operations. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * If/when new hardware solves this IPI problem, we should abandon 4362306a36Sopenharmony_ci * the current approach. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <linux/module.h> 4862306a36Sopenharmony_ci#include <linux/slab.h> 4962306a36Sopenharmony_ci#include <linux/sysctl.h> 5062306a36Sopenharmony_ci#include <linux/device.h> 5162306a36Sopenharmony_ci#include <linux/delay.h> 5262306a36Sopenharmony_ci#include <linux/reboot.h> 5362306a36Sopenharmony_ci#include <linux/kdebug.h> 5462306a36Sopenharmony_ci#include <linux/kthread.h> 5562306a36Sopenharmony_ci#include "xpc.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#ifdef CONFIG_X86_64 5862306a36Sopenharmony_ci#include <asm/traps.h> 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* define two XPC debug device structures to be used with dev_dbg() et al */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct device_driver xpc_dbg_name = { 6462306a36Sopenharmony_ci .name = "xpc" 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic struct device xpc_part_dbg_subname = { 6862306a36Sopenharmony_ci .init_name = "", /* set to "part" at xpc_init() time */ 6962306a36Sopenharmony_ci .driver = &xpc_dbg_name 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct device xpc_chan_dbg_subname = { 7362306a36Sopenharmony_ci .init_name = "", /* set to "chan" at xpc_init() time */ 7462306a36Sopenharmony_ci .driver = &xpc_dbg_name 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct device *xpc_part = &xpc_part_dbg_subname; 7862306a36Sopenharmony_cistruct device *xpc_chan = &xpc_chan_dbg_subname; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int xpc_kdebug_ignore; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* systune related variables for /proc/sys directories */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL; 8562306a36Sopenharmony_cistatic int xpc_hb_min_interval = 1; 8662306a36Sopenharmony_cistatic int xpc_hb_max_interval = 10; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL; 8962306a36Sopenharmony_cistatic int xpc_hb_check_min_interval = 10; 9062306a36Sopenharmony_cistatic int xpc_hb_check_max_interval = 120; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciint xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT; 9362306a36Sopenharmony_cistatic int xpc_disengage_min_timelimit; /* = 0 */ 9462306a36Sopenharmony_cistatic int xpc_disengage_max_timelimit = 120; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct ctl_table xpc_sys_xpc_hb[] = { 9762306a36Sopenharmony_ci { 9862306a36Sopenharmony_ci .procname = "hb_interval", 9962306a36Sopenharmony_ci .data = &xpc_hb_interval, 10062306a36Sopenharmony_ci .maxlen = sizeof(int), 10162306a36Sopenharmony_ci .mode = 0644, 10262306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 10362306a36Sopenharmony_ci .extra1 = &xpc_hb_min_interval, 10462306a36Sopenharmony_ci .extra2 = &xpc_hb_max_interval}, 10562306a36Sopenharmony_ci { 10662306a36Sopenharmony_ci .procname = "hb_check_interval", 10762306a36Sopenharmony_ci .data = &xpc_hb_check_interval, 10862306a36Sopenharmony_ci .maxlen = sizeof(int), 10962306a36Sopenharmony_ci .mode = 0644, 11062306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 11162306a36Sopenharmony_ci .extra1 = &xpc_hb_check_min_interval, 11262306a36Sopenharmony_ci .extra2 = &xpc_hb_check_max_interval}, 11362306a36Sopenharmony_ci {} 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_cistatic struct ctl_table xpc_sys_xpc[] = { 11662306a36Sopenharmony_ci { 11762306a36Sopenharmony_ci .procname = "disengage_timelimit", 11862306a36Sopenharmony_ci .data = &xpc_disengage_timelimit, 11962306a36Sopenharmony_ci .maxlen = sizeof(int), 12062306a36Sopenharmony_ci .mode = 0644, 12162306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 12262306a36Sopenharmony_ci .extra1 = &xpc_disengage_min_timelimit, 12362306a36Sopenharmony_ci .extra2 = &xpc_disengage_max_timelimit}, 12462306a36Sopenharmony_ci {} 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct ctl_table_header *xpc_sysctl; 12862306a36Sopenharmony_cistatic struct ctl_table_header *xpc_sysctl_hb; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* non-zero if any remote partition disengage was timed out */ 13162306a36Sopenharmony_ciint xpc_disengage_timedout; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* #of activate IRQs received and not yet processed */ 13462306a36Sopenharmony_ciint xpc_activate_IRQ_rcvd; 13562306a36Sopenharmony_ciDEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* IRQ handler notifies this wait queue on receipt of an IRQ */ 13862306a36Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic unsigned long xpc_hb_check_timeout; 14162306a36Sopenharmony_cistatic struct timer_list xpc_hb_timer; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* notification that the xpc_hb_checker thread has exited */ 14462306a36Sopenharmony_cistatic DECLARE_COMPLETION(xpc_hb_checker_exited); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* notification that the xpc_discovery thread has exited */ 14762306a36Sopenharmony_cistatic DECLARE_COMPLETION(xpc_discovery_exited); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int xpc_system_reboot(struct notifier_block *, unsigned long, void *); 15262306a36Sopenharmony_cistatic struct notifier_block xpc_reboot_notifier = { 15362306a36Sopenharmony_ci .notifier_call = xpc_system_reboot, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int xpc_system_die(struct notifier_block *, unsigned long, void *); 15762306a36Sopenharmony_cistatic struct notifier_block xpc_die_notifier = { 15862306a36Sopenharmony_ci .notifier_call = xpc_system_die, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistruct xpc_arch_operations xpc_arch_ops; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Timer function to enforce the timelimit on the partition disengage. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic void 16762306a36Sopenharmony_cixpc_timeout_partition_disengage(struct timer_list *t) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct xpc_partition *part = from_timer(part, t, disengage_timer); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci DBUG_ON(time_is_after_jiffies(part->disengage_timeout)); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci xpc_partition_disengaged_from_timer(part); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci DBUG_ON(part->disengage_timeout != 0); 17662306a36Sopenharmony_ci DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part))); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Timer to produce the heartbeat. The timer structures function is 18162306a36Sopenharmony_ci * already set when this is initially called. A tunable is used to 18262306a36Sopenharmony_ci * specify when the next timeout should occur. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic void 18562306a36Sopenharmony_cixpc_hb_beater(struct timer_list *unused) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci xpc_arch_ops.increment_heartbeat(); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) 19062306a36Sopenharmony_ci wake_up_interruptible(&xpc_activate_IRQ_wq); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ); 19362306a36Sopenharmony_ci add_timer(&xpc_hb_timer); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void 19762306a36Sopenharmony_cixpc_start_hb_beater(void) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci xpc_arch_ops.heartbeat_init(); 20062306a36Sopenharmony_ci timer_setup(&xpc_hb_timer, xpc_hb_beater, 0); 20162306a36Sopenharmony_ci xpc_hb_beater(NULL); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void 20562306a36Sopenharmony_cixpc_stop_hb_beater(void) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci del_timer_sync(&xpc_hb_timer); 20862306a36Sopenharmony_ci xpc_arch_ops.heartbeat_exit(); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * At periodic intervals, scan through all active partitions and ensure 21362306a36Sopenharmony_ci * their heartbeat is still active. If not, the partition is deactivated. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_cistatic void 21662306a36Sopenharmony_cixpc_check_remote_hb(void) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct xpc_partition *part; 21962306a36Sopenharmony_ci short partid; 22062306a36Sopenharmony_ci enum xp_retval ret; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (xpc_exiting) 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (partid == xp_partition_id) 22862306a36Sopenharmony_ci continue; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci part = &xpc_partitions[partid]; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (part->act_state == XPC_P_AS_INACTIVE || 23362306a36Sopenharmony_ci part->act_state == XPC_P_AS_DEACTIVATING) { 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ret = xpc_arch_ops.get_remote_heartbeat(part); 23862306a36Sopenharmony_ci if (ret != xpSuccess) 23962306a36Sopenharmony_ci XPC_DEACTIVATE_PARTITION(part, ret); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * This thread is responsible for nearly all of the partition 24562306a36Sopenharmony_ci * activation/deactivation. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic int 24862306a36Sopenharmony_cixpc_hb_checker(void *ignore) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int force_IRQ = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* this thread was marked active by xpc_hb_init() */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* set our heartbeating to other partitions into motion */ 25762306a36Sopenharmony_ci xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); 25862306a36Sopenharmony_ci xpc_start_hb_beater(); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci while (!xpc_exiting) { 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have " 26362306a36Sopenharmony_ci "been received\n", 26462306a36Sopenharmony_ci (int)(xpc_hb_check_timeout - jiffies), 26562306a36Sopenharmony_ci xpc_activate_IRQ_rcvd); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* checking of remote heartbeats is skewed by IRQ handling */ 26862306a36Sopenharmony_ci if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) { 26962306a36Sopenharmony_ci xpc_hb_check_timeout = jiffies + 27062306a36Sopenharmony_ci (xpc_hb_check_interval * HZ); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci dev_dbg(xpc_part, "checking remote heartbeats\n"); 27362306a36Sopenharmony_ci xpc_check_remote_hb(); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* check for outstanding IRQs */ 27762306a36Sopenharmony_ci if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) { 27862306a36Sopenharmony_ci force_IRQ = 0; 27962306a36Sopenharmony_ci dev_dbg(xpc_part, "processing activate IRQs " 28062306a36Sopenharmony_ci "received\n"); 28162306a36Sopenharmony_ci xpc_arch_ops.process_activate_IRQ_rcvd(); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* wait for IRQ or timeout */ 28562306a36Sopenharmony_ci (void)wait_event_interruptible(xpc_activate_IRQ_wq, 28662306a36Sopenharmony_ci (time_is_before_eq_jiffies( 28762306a36Sopenharmony_ci xpc_hb_check_timeout) || 28862306a36Sopenharmony_ci xpc_activate_IRQ_rcvd > 0 || 28962306a36Sopenharmony_ci xpc_exiting)); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci xpc_stop_hb_beater(); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci dev_dbg(xpc_part, "heartbeat checker is exiting\n"); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* mark this thread as having exited */ 29762306a36Sopenharmony_ci complete(&xpc_hb_checker_exited); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * This thread will attempt to discover other partitions to activate 30362306a36Sopenharmony_ci * based on info provided by SAL. This new thread is short lived and 30462306a36Sopenharmony_ci * will exit once discovery is complete. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic int 30762306a36Sopenharmony_cixpc_initiate_discovery(void *ignore) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci xpc_discovery(); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dev_dbg(xpc_part, "discovery thread is exiting\n"); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* mark this thread as having exited */ 31462306a36Sopenharmony_ci complete(&xpc_discovery_exited); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * The first kthread assigned to a newly activated partition is the one 32062306a36Sopenharmony_ci * created by XPC HB with which it calls xpc_activating(). XPC hangs on to 32162306a36Sopenharmony_ci * that kthread until the partition is brought down, at which time that kthread 32262306a36Sopenharmony_ci * returns back to XPC HB. (The return of that kthread will signify to XPC HB 32362306a36Sopenharmony_ci * that XPC has dismantled all communication infrastructure for the associated 32462306a36Sopenharmony_ci * partition.) This kthread becomes the channel manager for that partition. 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Each active partition has a channel manager, who, besides connecting and 32762306a36Sopenharmony_ci * disconnecting channels, will ensure that each of the partition's connected 32862306a36Sopenharmony_ci * channels has the required number of assigned kthreads to get the work done. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic void 33162306a36Sopenharmony_cixpc_channel_mgr(struct xpc_partition *part) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci while (part->act_state != XPC_P_AS_DEACTIVATING || 33462306a36Sopenharmony_ci atomic_read(&part->nchannels_active) > 0 || 33562306a36Sopenharmony_ci !xpc_partition_disengaged(part)) { 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci xpc_process_sent_chctl_flags(part); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * Wait until we've been requested to activate kthreads or 34162306a36Sopenharmony_ci * all of the channel's message queues have been torn down or 34262306a36Sopenharmony_ci * a signal is pending. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * The channel_mgr_requests is set to 1 after being awakened, 34562306a36Sopenharmony_ci * This is done to prevent the channel mgr from making one pass 34662306a36Sopenharmony_ci * through the loop for each request, since he will 34762306a36Sopenharmony_ci * be servicing all the requests in one pass. The reason it's 34862306a36Sopenharmony_ci * set to 1 instead of 0 is so that other kthreads will know 34962306a36Sopenharmony_ci * that the channel mgr is running and won't bother trying to 35062306a36Sopenharmony_ci * wake him up. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci atomic_dec(&part->channel_mgr_requests); 35362306a36Sopenharmony_ci (void)wait_event_interruptible(part->channel_mgr_wq, 35462306a36Sopenharmony_ci (atomic_read(&part->channel_mgr_requests) > 0 || 35562306a36Sopenharmony_ci part->chctl.all_flags != 0 || 35662306a36Sopenharmony_ci (part->act_state == XPC_P_AS_DEACTIVATING && 35762306a36Sopenharmony_ci atomic_read(&part->nchannels_active) == 0 && 35862306a36Sopenharmony_ci xpc_partition_disengaged(part)))); 35962306a36Sopenharmony_ci atomic_set(&part->channel_mgr_requests, 1); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Guarantee that the kzalloc'd memory is cacheline aligned. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_civoid * 36762306a36Sopenharmony_cixpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci /* see if kzalloc will give us cachline aligned memory by default */ 37062306a36Sopenharmony_ci *base = kzalloc(size, flags); 37162306a36Sopenharmony_ci if (*base == NULL) 37262306a36Sopenharmony_ci return NULL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) 37562306a36Sopenharmony_ci return *base; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci kfree(*base); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* nope, we'll have to do it ourselves */ 38062306a36Sopenharmony_ci *base = kzalloc(size + L1_CACHE_BYTES, flags); 38162306a36Sopenharmony_ci if (*base == NULL) 38262306a36Sopenharmony_ci return NULL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return (void *)L1_CACHE_ALIGN((u64)*base); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * Setup the channel structures necessary to support XPartition Communication 38962306a36Sopenharmony_ci * between the specified remote partition and the local one. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistatic enum xp_retval 39262306a36Sopenharmony_cixpc_setup_ch_structures(struct xpc_partition *part) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci enum xp_retval ret; 39562306a36Sopenharmony_ci int ch_number; 39662306a36Sopenharmony_ci struct xpc_channel *ch; 39762306a36Sopenharmony_ci short partid = XPC_PARTID(part); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * Allocate all of the channel structures as a contiguous chunk of 40162306a36Sopenharmony_ci * memory. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci DBUG_ON(part->channels != NULL); 40462306a36Sopenharmony_ci part->channels = kcalloc(XPC_MAX_NCHANNELS, 40562306a36Sopenharmony_ci sizeof(struct xpc_channel), 40662306a36Sopenharmony_ci GFP_KERNEL); 40762306a36Sopenharmony_ci if (part->channels == NULL) { 40862306a36Sopenharmony_ci dev_err(xpc_chan, "can't get memory for channels\n"); 40962306a36Sopenharmony_ci return xpNoMemory; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* allocate the remote open and close args */ 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci part->remote_openclose_args = 41562306a36Sopenharmony_ci xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE, 41662306a36Sopenharmony_ci GFP_KERNEL, &part-> 41762306a36Sopenharmony_ci remote_openclose_args_base); 41862306a36Sopenharmony_ci if (part->remote_openclose_args == NULL) { 41962306a36Sopenharmony_ci dev_err(xpc_chan, "can't get memory for remote connect args\n"); 42062306a36Sopenharmony_ci ret = xpNoMemory; 42162306a36Sopenharmony_ci goto out_1; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci part->chctl.all_flags = 0; 42562306a36Sopenharmony_ci spin_lock_init(&part->chctl_lock); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci atomic_set(&part->channel_mgr_requests, 1); 42862306a36Sopenharmony_ci init_waitqueue_head(&part->channel_mgr_wq); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci part->nchannels = XPC_MAX_NCHANNELS; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci atomic_set(&part->nchannels_active, 0); 43362306a36Sopenharmony_ci atomic_set(&part->nchannels_engaged, 0); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci for (ch_number = 0; ch_number < part->nchannels; ch_number++) { 43662306a36Sopenharmony_ci ch = &part->channels[ch_number]; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ch->partid = partid; 43962306a36Sopenharmony_ci ch->number = ch_number; 44062306a36Sopenharmony_ci ch->flags = XPC_C_DISCONNECTED; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci atomic_set(&ch->kthreads_assigned, 0); 44362306a36Sopenharmony_ci atomic_set(&ch->kthreads_idle, 0); 44462306a36Sopenharmony_ci atomic_set(&ch->kthreads_active, 0); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci atomic_set(&ch->references, 0); 44762306a36Sopenharmony_ci atomic_set(&ch->n_to_notify, 0); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci spin_lock_init(&ch->lock); 45062306a36Sopenharmony_ci init_completion(&ch->wdisconnect_wait); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci atomic_set(&ch->n_on_msg_allocate_wq, 0); 45362306a36Sopenharmony_ci init_waitqueue_head(&ch->msg_allocate_wq); 45462306a36Sopenharmony_ci init_waitqueue_head(&ch->idle_wq); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = xpc_arch_ops.setup_ch_structures(part); 45862306a36Sopenharmony_ci if (ret != xpSuccess) 45962306a36Sopenharmony_ci goto out_2; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* 46262306a36Sopenharmony_ci * With the setting of the partition setup_state to XPC_P_SS_SETUP, 46362306a36Sopenharmony_ci * we're declaring that this partition is ready to go. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci part->setup_state = XPC_P_SS_SETUP; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return xpSuccess; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* setup of ch structures failed */ 47062306a36Sopenharmony_ciout_2: 47162306a36Sopenharmony_ci kfree(part->remote_openclose_args_base); 47262306a36Sopenharmony_ci part->remote_openclose_args = NULL; 47362306a36Sopenharmony_ciout_1: 47462306a36Sopenharmony_ci kfree(part->channels); 47562306a36Sopenharmony_ci part->channels = NULL; 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* 48062306a36Sopenharmony_ci * Teardown the channel structures necessary to support XPartition Communication 48162306a36Sopenharmony_ci * between the specified remote partition and the local one. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic void 48462306a36Sopenharmony_cixpc_teardown_ch_structures(struct xpc_partition *part) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci DBUG_ON(atomic_read(&part->nchannels_engaged) != 0); 48762306a36Sopenharmony_ci DBUG_ON(atomic_read(&part->nchannels_active) != 0); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * Make this partition inaccessible to local processes by marking it 49162306a36Sopenharmony_ci * as no longer setup. Then wait before proceeding with the teardown 49262306a36Sopenharmony_ci * until all existing references cease. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci DBUG_ON(part->setup_state != XPC_P_SS_SETUP); 49562306a36Sopenharmony_ci part->setup_state = XPC_P_SS_WTEARDOWN; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* now we can begin tearing down the infrastructure */ 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci xpc_arch_ops.teardown_ch_structures(part); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci kfree(part->remote_openclose_args_base); 50462306a36Sopenharmony_ci part->remote_openclose_args = NULL; 50562306a36Sopenharmony_ci kfree(part->channels); 50662306a36Sopenharmony_ci part->channels = NULL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci part->setup_state = XPC_P_SS_TORNDOWN; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* 51262306a36Sopenharmony_ci * When XPC HB determines that a partition has come up, it will create a new 51362306a36Sopenharmony_ci * kthread and that kthread will call this function to attempt to set up the 51462306a36Sopenharmony_ci * basic infrastructure used for Cross Partition Communication with the newly 51562306a36Sopenharmony_ci * upped partition. 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * The kthread that was created by XPC HB and which setup the XPC 51862306a36Sopenharmony_ci * infrastructure will remain assigned to the partition becoming the channel 51962306a36Sopenharmony_ci * manager for that partition until the partition is deactivating, at which 52062306a36Sopenharmony_ci * time the kthread will teardown the XPC infrastructure and then exit. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic int 52362306a36Sopenharmony_cixpc_activating(void *__partid) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci short partid = (u64)__partid; 52662306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[partid]; 52762306a36Sopenharmony_ci unsigned long irq_flags; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci DBUG_ON(partid < 0 || partid >= xp_max_npartitions); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci spin_lock_irqsave(&part->act_lock, irq_flags); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (part->act_state == XPC_P_AS_DEACTIVATING) { 53462306a36Sopenharmony_ci part->act_state = XPC_P_AS_INACTIVE; 53562306a36Sopenharmony_ci spin_unlock_irqrestore(&part->act_lock, irq_flags); 53662306a36Sopenharmony_ci part->remote_rp_pa = 0; 53762306a36Sopenharmony_ci return 0; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* indicate the thread is activating */ 54162306a36Sopenharmony_ci DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ); 54262306a36Sopenharmony_ci part->act_state = XPC_P_AS_ACTIVATING; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci XPC_SET_REASON(part, 0, 0); 54562306a36Sopenharmony_ci spin_unlock_irqrestore(&part->act_lock, irq_flags); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci dev_dbg(xpc_part, "activating partition %d\n", partid); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci xpc_arch_ops.allow_hb(partid); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (xpc_setup_ch_structures(part) == xpSuccess) { 55262306a36Sopenharmony_ci (void)xpc_part_ref(part); /* this will always succeed */ 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (xpc_arch_ops.make_first_contact(part) == xpSuccess) { 55562306a36Sopenharmony_ci xpc_mark_partition_active(part); 55662306a36Sopenharmony_ci xpc_channel_mgr(part); 55762306a36Sopenharmony_ci /* won't return until partition is deactivating */ 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci xpc_part_deref(part); 56162306a36Sopenharmony_ci xpc_teardown_ch_structures(part); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci xpc_arch_ops.disallow_hb(partid); 56562306a36Sopenharmony_ci xpc_mark_partition_inactive(part); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (part->reason == xpReactivating) { 56862306a36Sopenharmony_ci /* interrupting ourselves results in activating partition */ 56962306a36Sopenharmony_ci xpc_arch_ops.request_partition_reactivation(part); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_civoid 57662306a36Sopenharmony_cixpc_activate_partition(struct xpc_partition *part) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci short partid = XPC_PARTID(part); 57962306a36Sopenharmony_ci unsigned long irq_flags; 58062306a36Sopenharmony_ci struct task_struct *kthread; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci spin_lock_irqsave(&part->act_lock, irq_flags); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci DBUG_ON(part->act_state != XPC_P_AS_INACTIVE); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci part->act_state = XPC_P_AS_ACTIVATION_REQ; 58762306a36Sopenharmony_ci XPC_SET_REASON(part, xpCloneKThread, __LINE__); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci spin_unlock_irqrestore(&part->act_lock, irq_flags); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d", 59262306a36Sopenharmony_ci partid); 59362306a36Sopenharmony_ci if (IS_ERR(kthread)) { 59462306a36Sopenharmony_ci spin_lock_irqsave(&part->act_lock, irq_flags); 59562306a36Sopenharmony_ci part->act_state = XPC_P_AS_INACTIVE; 59662306a36Sopenharmony_ci XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__); 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&part->act_lock, irq_flags); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_civoid 60262306a36Sopenharmony_cixpc_activate_kthreads(struct xpc_channel *ch, int needed) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci int idle = atomic_read(&ch->kthreads_idle); 60562306a36Sopenharmony_ci int assigned = atomic_read(&ch->kthreads_assigned); 60662306a36Sopenharmony_ci int wakeup; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci DBUG_ON(needed <= 0); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (idle > 0) { 61162306a36Sopenharmony_ci wakeup = (needed > idle) ? idle : needed; 61262306a36Sopenharmony_ci needed -= wakeup; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, " 61562306a36Sopenharmony_ci "channel=%d\n", wakeup, ch->partid, ch->number); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* only wakeup the requested number of kthreads */ 61862306a36Sopenharmony_ci wake_up_nr(&ch->idle_wq, wakeup); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (needed <= 0) 62262306a36Sopenharmony_ci return; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (needed + assigned > ch->kthreads_assigned_limit) { 62562306a36Sopenharmony_ci needed = ch->kthreads_assigned_limit - assigned; 62662306a36Sopenharmony_ci if (needed <= 0) 62762306a36Sopenharmony_ci return; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n", 63162306a36Sopenharmony_ci needed, ch->partid, ch->number); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci xpc_create_kthreads(ch, needed, 0); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci/* 63762306a36Sopenharmony_ci * This function is where XPC's kthreads wait for messages to deliver. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_cistatic void 64062306a36Sopenharmony_cixpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci int (*n_of_deliverable_payloads) (struct xpc_channel *) = 64362306a36Sopenharmony_ci xpc_arch_ops.n_of_deliverable_payloads; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci do { 64662306a36Sopenharmony_ci /* deliver messages to their intended recipients */ 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci while (n_of_deliverable_payloads(ch) > 0 && 64962306a36Sopenharmony_ci !(ch->flags & XPC_C_DISCONNECTING)) { 65062306a36Sopenharmony_ci xpc_deliver_payload(ch); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (atomic_inc_return(&ch->kthreads_idle) > 65462306a36Sopenharmony_ci ch->kthreads_idle_limit) { 65562306a36Sopenharmony_ci /* too many idle kthreads on this channel */ 65662306a36Sopenharmony_ci atomic_dec(&ch->kthreads_idle); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci dev_dbg(xpc_chan, "idle kthread calling " 66162306a36Sopenharmony_ci "wait_event_interruptible_exclusive()\n"); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci (void)wait_event_interruptible_exclusive(ch->idle_wq, 66462306a36Sopenharmony_ci (n_of_deliverable_payloads(ch) > 0 || 66562306a36Sopenharmony_ci (ch->flags & XPC_C_DISCONNECTING))); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci atomic_dec(&ch->kthreads_idle); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci } while (!(ch->flags & XPC_C_DISCONNECTING)); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int 67362306a36Sopenharmony_cixpc_kthread_start(void *args) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci short partid = XPC_UNPACK_ARG1(args); 67662306a36Sopenharmony_ci u16 ch_number = XPC_UNPACK_ARG2(args); 67762306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[partid]; 67862306a36Sopenharmony_ci struct xpc_channel *ch; 67962306a36Sopenharmony_ci int n_needed; 68062306a36Sopenharmony_ci unsigned long irq_flags; 68162306a36Sopenharmony_ci int (*n_of_deliverable_payloads) (struct xpc_channel *) = 68262306a36Sopenharmony_ci xpc_arch_ops.n_of_deliverable_payloads; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n", 68562306a36Sopenharmony_ci partid, ch_number); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci ch = &part->channels[ch_number]; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!(ch->flags & XPC_C_DISCONNECTING)) { 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* let registerer know that connection has been established */ 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 69462306a36Sopenharmony_ci if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) { 69562306a36Sopenharmony_ci ch->flags |= XPC_C_CONNECTEDCALLOUT; 69662306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci xpc_connected_callout(ch); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 70162306a36Sopenharmony_ci ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE; 70262306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * It is possible that while the callout was being 70662306a36Sopenharmony_ci * made that the remote partition sent some messages. 70762306a36Sopenharmony_ci * If that is the case, we may need to activate 70862306a36Sopenharmony_ci * additional kthreads to help deliver them. We only 70962306a36Sopenharmony_ci * need one less than total #of messages to deliver. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci n_needed = n_of_deliverable_payloads(ch) - 1; 71262306a36Sopenharmony_ci if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING)) 71362306a36Sopenharmony_ci xpc_activate_kthreads(ch, n_needed); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci xpc_kthread_waitmsgs(part, ch); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* let registerer know that connection is disconnecting */ 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 72562306a36Sopenharmony_ci if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && 72662306a36Sopenharmony_ci !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { 72762306a36Sopenharmony_ci ch->flags |= XPC_C_DISCONNECTINGCALLOUT; 72862306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci xpc_disconnect_callout(ch, xpDisconnecting); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 73362306a36Sopenharmony_ci ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (atomic_dec_return(&ch->kthreads_assigned) == 0 && 73862306a36Sopenharmony_ci atomic_dec_return(&part->nchannels_engaged) == 0) { 73962306a36Sopenharmony_ci xpc_arch_ops.indicate_partition_disengaged(part); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci xpc_msgqueue_deref(ch); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n", 74562306a36Sopenharmony_ci partid, ch_number); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci xpc_part_deref(part); 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* 75262306a36Sopenharmony_ci * For each partition that XPC has established communications with, there is 75362306a36Sopenharmony_ci * a minimum of one kernel thread assigned to perform any operation that 75462306a36Sopenharmony_ci * may potentially sleep or block (basically the callouts to the asynchronous 75562306a36Sopenharmony_ci * functions registered via xpc_connect()). 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Additional kthreads are created and destroyed by XPC as the workload 75862306a36Sopenharmony_ci * demands. 75962306a36Sopenharmony_ci * 76062306a36Sopenharmony_ci * A kthread is assigned to one of the active channels that exists for a given 76162306a36Sopenharmony_ci * partition. 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_civoid 76462306a36Sopenharmony_cixpc_create_kthreads(struct xpc_channel *ch, int needed, 76562306a36Sopenharmony_ci int ignore_disconnecting) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci unsigned long irq_flags; 76862306a36Sopenharmony_ci u64 args = XPC_PACK_ARGS(ch->partid, ch->number); 76962306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[ch->partid]; 77062306a36Sopenharmony_ci struct task_struct *kthread; 77162306a36Sopenharmony_ci void (*indicate_partition_disengaged) (struct xpc_partition *) = 77262306a36Sopenharmony_ci xpc_arch_ops.indicate_partition_disengaged; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci while (needed-- > 0) { 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* 77762306a36Sopenharmony_ci * The following is done on behalf of the newly created 77862306a36Sopenharmony_ci * kthread. That kthread is responsible for doing the 77962306a36Sopenharmony_ci * counterpart to the following before it exits. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci if (ignore_disconnecting) { 78262306a36Sopenharmony_ci if (!atomic_inc_not_zero(&ch->kthreads_assigned)) { 78362306a36Sopenharmony_ci /* kthreads assigned had gone to zero */ 78462306a36Sopenharmony_ci BUG_ON(!(ch->flags & 78562306a36Sopenharmony_ci XPC_C_DISCONNECTINGCALLOUT_MADE)); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci } else if (ch->flags & XPC_C_DISCONNECTING) { 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 && 79362306a36Sopenharmony_ci atomic_inc_return(&part->nchannels_engaged) == 1) { 79462306a36Sopenharmony_ci xpc_arch_ops.indicate_partition_engaged(part); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci (void)xpc_part_ref(part); 79762306a36Sopenharmony_ci xpc_msgqueue_ref(ch); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci kthread = kthread_run(xpc_kthread_start, (void *)args, 80062306a36Sopenharmony_ci "xpc%02dc%d", ch->partid, ch->number); 80162306a36Sopenharmony_ci if (IS_ERR(kthread)) { 80262306a36Sopenharmony_ci /* the fork failed */ 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* 80562306a36Sopenharmony_ci * NOTE: if (ignore_disconnecting && 80662306a36Sopenharmony_ci * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true, 80762306a36Sopenharmony_ci * then we'll deadlock if all other kthreads assigned 80862306a36Sopenharmony_ci * to this channel are blocked in the channel's 80962306a36Sopenharmony_ci * registerer, because the only thing that will unblock 81062306a36Sopenharmony_ci * them is the xpDisconnecting callout that this 81162306a36Sopenharmony_ci * failed kthread_run() would have made. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (atomic_dec_return(&ch->kthreads_assigned) == 0 && 81562306a36Sopenharmony_ci atomic_dec_return(&part->nchannels_engaged) == 0) { 81662306a36Sopenharmony_ci indicate_partition_disengaged(part); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci xpc_msgqueue_deref(ch); 81962306a36Sopenharmony_ci xpc_part_deref(part); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (atomic_read(&ch->kthreads_assigned) < 82262306a36Sopenharmony_ci ch->kthreads_idle_limit) { 82362306a36Sopenharmony_ci /* 82462306a36Sopenharmony_ci * Flag this as an error only if we have an 82562306a36Sopenharmony_ci * insufficient #of kthreads for the channel 82662306a36Sopenharmony_ci * to function. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 82962306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, 83062306a36Sopenharmony_ci &irq_flags); 83162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_civoid 83962306a36Sopenharmony_cixpc_disconnect_wait(int ch_number) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci unsigned long irq_flags; 84262306a36Sopenharmony_ci short partid; 84362306a36Sopenharmony_ci struct xpc_partition *part; 84462306a36Sopenharmony_ci struct xpc_channel *ch; 84562306a36Sopenharmony_ci int wakeup_channel_mgr; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* now wait for all callouts to the caller's function to cease */ 84862306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 84962306a36Sopenharmony_ci part = &xpc_partitions[partid]; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (!xpc_part_ref(part)) 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ch = &part->channels[ch_number]; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (!(ch->flags & XPC_C_WDISCONNECT)) { 85762306a36Sopenharmony_ci xpc_part_deref(part); 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci wait_for_completion(&ch->wdisconnect_wait); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 86462306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); 86562306a36Sopenharmony_ci wakeup_channel_mgr = 0; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (ch->delayed_chctl_flags) { 86862306a36Sopenharmony_ci if (part->act_state != XPC_P_AS_DEACTIVATING) { 86962306a36Sopenharmony_ci spin_lock(&part->chctl_lock); 87062306a36Sopenharmony_ci part->chctl.flags[ch->number] |= 87162306a36Sopenharmony_ci ch->delayed_chctl_flags; 87262306a36Sopenharmony_ci spin_unlock(&part->chctl_lock); 87362306a36Sopenharmony_ci wakeup_channel_mgr = 1; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci ch->delayed_chctl_flags = 0; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci ch->flags &= ~XPC_C_WDISCONNECT; 87962306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (wakeup_channel_mgr) 88262306a36Sopenharmony_ci xpc_wakeup_channel_mgr(part); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci xpc_part_deref(part); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int 88962306a36Sopenharmony_cixpc_setup_partitions(void) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci short partid; 89262306a36Sopenharmony_ci struct xpc_partition *part; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci xpc_partitions = kcalloc(xp_max_npartitions, 89562306a36Sopenharmony_ci sizeof(struct xpc_partition), 89662306a36Sopenharmony_ci GFP_KERNEL); 89762306a36Sopenharmony_ci if (xpc_partitions == NULL) { 89862306a36Sopenharmony_ci dev_err(xpc_part, "can't get memory for partition structure\n"); 89962306a36Sopenharmony_ci return -ENOMEM; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * The first few fields of each entry of xpc_partitions[] need to 90462306a36Sopenharmony_ci * be initialized now so that calls to xpc_connect() and 90562306a36Sopenharmony_ci * xpc_disconnect() can be made prior to the activation of any remote 90662306a36Sopenharmony_ci * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE 90762306a36Sopenharmony_ci * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING 90862306a36Sopenharmony_ci * PARTITION HAS BEEN ACTIVATED. 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 91162306a36Sopenharmony_ci part = &xpc_partitions[partid]; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part)); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci part->activate_IRQ_rcvd = 0; 91662306a36Sopenharmony_ci spin_lock_init(&part->act_lock); 91762306a36Sopenharmony_ci part->act_state = XPC_P_AS_INACTIVE; 91862306a36Sopenharmony_ci XPC_SET_REASON(part, 0, 0); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci timer_setup(&part->disengage_timer, 92162306a36Sopenharmony_ci xpc_timeout_partition_disengage, 0); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci part->setup_state = XPC_P_SS_UNSET; 92462306a36Sopenharmony_ci init_waitqueue_head(&part->teardown_wq); 92562306a36Sopenharmony_ci atomic_set(&part->references, 0); 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return xpc_arch_ops.setup_partitions(); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void 93262306a36Sopenharmony_cixpc_teardown_partitions(void) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci xpc_arch_ops.teardown_partitions(); 93562306a36Sopenharmony_ci kfree(xpc_partitions); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void 93962306a36Sopenharmony_cixpc_do_exit(enum xp_retval reason) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci short partid; 94262306a36Sopenharmony_ci int active_part_count, printed_waiting_msg = 0; 94362306a36Sopenharmony_ci struct xpc_partition *part; 94462306a36Sopenharmony_ci unsigned long printmsg_time, disengage_timeout = 0; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */ 94762306a36Sopenharmony_ci DBUG_ON(xpc_exiting == 1); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * Let the heartbeat checker thread and the discovery thread 95162306a36Sopenharmony_ci * (if one is running) know that they should exit. Also wake up 95262306a36Sopenharmony_ci * the heartbeat checker thread in case it's sleeping. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ci xpc_exiting = 1; 95562306a36Sopenharmony_ci wake_up_interruptible(&xpc_activate_IRQ_wq); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* wait for the discovery thread to exit */ 95862306a36Sopenharmony_ci wait_for_completion(&xpc_discovery_exited); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* wait for the heartbeat checker thread to exit */ 96162306a36Sopenharmony_ci wait_for_completion(&xpc_hb_checker_exited); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* sleep for a 1/3 of a second or so */ 96462306a36Sopenharmony_ci (void)msleep_interruptible(300); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* wait for all partitions to become inactive */ 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); 96962306a36Sopenharmony_ci xpc_disengage_timedout = 0; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci do { 97262306a36Sopenharmony_ci active_part_count = 0; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 97562306a36Sopenharmony_ci part = &xpc_partitions[partid]; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (xpc_partition_disengaged(part) && 97862306a36Sopenharmony_ci part->act_state == XPC_P_AS_INACTIVE) { 97962306a36Sopenharmony_ci continue; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci active_part_count++; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci XPC_DEACTIVATE_PARTITION(part, reason); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (part->disengage_timeout > disengage_timeout) 98762306a36Sopenharmony_ci disengage_timeout = part->disengage_timeout; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (xpc_arch_ops.any_partition_engaged()) { 99162306a36Sopenharmony_ci if (time_is_before_jiffies(printmsg_time)) { 99262306a36Sopenharmony_ci dev_info(xpc_part, "waiting for remote " 99362306a36Sopenharmony_ci "partitions to deactivate, timeout in " 99462306a36Sopenharmony_ci "%ld seconds\n", (disengage_timeout - 99562306a36Sopenharmony_ci jiffies) / HZ); 99662306a36Sopenharmony_ci printmsg_time = jiffies + 99762306a36Sopenharmony_ci (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); 99862306a36Sopenharmony_ci printed_waiting_msg = 1; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci } else if (active_part_count > 0) { 100262306a36Sopenharmony_ci if (printed_waiting_msg) { 100362306a36Sopenharmony_ci dev_info(xpc_part, "waiting for local partition" 100462306a36Sopenharmony_ci " to deactivate\n"); 100562306a36Sopenharmony_ci printed_waiting_msg = 0; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci } else { 100962306a36Sopenharmony_ci if (!xpc_disengage_timedout) { 101062306a36Sopenharmony_ci dev_info(xpc_part, "all partitions have " 101162306a36Sopenharmony_ci "deactivated\n"); 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* sleep for a 1/3 of a second or so */ 101762306a36Sopenharmony_ci (void)msleep_interruptible(300); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci } while (1); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci DBUG_ON(xpc_arch_ops.any_partition_engaged()); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci xpc_teardown_rsvd_page(); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (reason == xpUnloading) { 102662306a36Sopenharmony_ci (void)unregister_die_notifier(&xpc_die_notifier); 102762306a36Sopenharmony_ci (void)unregister_reboot_notifier(&xpc_reboot_notifier); 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* clear the interface to XPC's functions */ 103162306a36Sopenharmony_ci xpc_clear_interface(); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (xpc_sysctl) 103462306a36Sopenharmony_ci unregister_sysctl_table(xpc_sysctl); 103562306a36Sopenharmony_ci if (xpc_sysctl_hb) 103662306a36Sopenharmony_ci unregister_sysctl_table(xpc_sysctl_hb); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci xpc_teardown_partitions(); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (is_uv_system()) 104162306a36Sopenharmony_ci xpc_exit_uv(); 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci/* 104562306a36Sopenharmony_ci * This function is called when the system is being rebooted. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_cistatic int 104862306a36Sopenharmony_cixpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci enum xp_retval reason; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci switch (event) { 105362306a36Sopenharmony_ci case SYS_RESTART: 105462306a36Sopenharmony_ci reason = xpSystemReboot; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci case SYS_HALT: 105762306a36Sopenharmony_ci reason = xpSystemHalt; 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci case SYS_POWER_OFF: 106062306a36Sopenharmony_ci reason = xpSystemPoweroff; 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci default: 106362306a36Sopenharmony_ci reason = xpSystemGoingDown; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci xpc_do_exit(reason); 106762306a36Sopenharmony_ci return NOTIFY_DONE; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci/* Used to only allow one cpu to complete disconnect */ 107162306a36Sopenharmony_cistatic unsigned int xpc_die_disconnecting; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/* 107462306a36Sopenharmony_ci * Notify other partitions to deactivate from us by first disengaging from all 107562306a36Sopenharmony_ci * references to our memory. 107662306a36Sopenharmony_ci */ 107762306a36Sopenharmony_cistatic void 107862306a36Sopenharmony_cixpc_die_deactivate(void) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct xpc_partition *part; 108162306a36Sopenharmony_ci short partid; 108262306a36Sopenharmony_ci int any_engaged; 108362306a36Sopenharmony_ci long keep_waiting; 108462306a36Sopenharmony_ci long wait_to_print; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (cmpxchg(&xpc_die_disconnecting, 0, 1)) 108762306a36Sopenharmony_ci return; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* keep xpc_hb_checker thread from doing anything (just in case) */ 109062306a36Sopenharmony_ci xpc_exiting = 1; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */ 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 109562306a36Sopenharmony_ci part = &xpc_partitions[partid]; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (xpc_arch_ops.partition_engaged(partid) || 109862306a36Sopenharmony_ci part->act_state != XPC_P_AS_INACTIVE) { 109962306a36Sopenharmony_ci xpc_arch_ops.request_partition_deactivation(part); 110062306a36Sopenharmony_ci xpc_arch_ops.indicate_partition_disengaged(part); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* 110562306a36Sopenharmony_ci * Though we requested that all other partitions deactivate from us, 110662306a36Sopenharmony_ci * we only wait until they've all disengaged or we've reached the 110762306a36Sopenharmony_ci * defined timelimit. 110862306a36Sopenharmony_ci * 110962306a36Sopenharmony_ci * Given that one iteration through the following while-loop takes 111062306a36Sopenharmony_ci * approximately 200 microseconds, calculate the #of loops to take 111162306a36Sopenharmony_ci * before bailing and the #of loops before printing a waiting message. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci keep_waiting = xpc_disengage_timelimit * 1000 * 5; 111462306a36Sopenharmony_ci wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci while (1) { 111762306a36Sopenharmony_ci any_engaged = xpc_arch_ops.any_partition_engaged(); 111862306a36Sopenharmony_ci if (!any_engaged) { 111962306a36Sopenharmony_ci dev_info(xpc_part, "all partitions have deactivated\n"); 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (!keep_waiting--) { 112462306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; 112562306a36Sopenharmony_ci partid++) { 112662306a36Sopenharmony_ci if (xpc_arch_ops.partition_engaged(partid)) { 112762306a36Sopenharmony_ci dev_info(xpc_part, "deactivate from " 112862306a36Sopenharmony_ci "remote partition %d timed " 112962306a36Sopenharmony_ci "out\n", partid); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!wait_to_print--) { 113662306a36Sopenharmony_ci dev_info(xpc_part, "waiting for remote partitions to " 113762306a36Sopenharmony_ci "deactivate, timeout in %ld seconds\n", 113862306a36Sopenharmony_ci keep_waiting / (1000 * 5)); 113962306a36Sopenharmony_ci wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 114062306a36Sopenharmony_ci 1000 * 5; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci udelay(200); 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci/* 114862306a36Sopenharmony_ci * This function is called when the system is being restarted or halted due 114962306a36Sopenharmony_ci * to some sort of system failure. If this is the case we need to notify the 115062306a36Sopenharmony_ci * other partitions to disengage from all references to our memory. 115162306a36Sopenharmony_ci * This function can also be called when our heartbeater could be offlined 115262306a36Sopenharmony_ci * for a time. In this case we need to notify other partitions to not worry 115362306a36Sopenharmony_ci * about the lack of a heartbeat. 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_cistatic int 115662306a36Sopenharmony_cixpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci#ifdef CONFIG_IA64 /* !!! temporary kludge */ 115962306a36Sopenharmony_ci switch (event) { 116062306a36Sopenharmony_ci case DIE_MACHINE_RESTART: 116162306a36Sopenharmony_ci case DIE_MACHINE_HALT: 116262306a36Sopenharmony_ci xpc_die_deactivate(); 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci case DIE_KDEBUG_ENTER: 116662306a36Sopenharmony_ci /* Should lack of heartbeat be ignored by other partitions? */ 116762306a36Sopenharmony_ci if (!xpc_kdebug_ignore) 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci fallthrough; 117162306a36Sopenharmony_ci case DIE_MCA_MONARCH_ENTER: 117262306a36Sopenharmony_ci case DIE_INIT_MONARCH_ENTER: 117362306a36Sopenharmony_ci xpc_arch_ops.offline_heartbeat(); 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci case DIE_KDEBUG_LEAVE: 117762306a36Sopenharmony_ci /* Is lack of heartbeat being ignored by other partitions? */ 117862306a36Sopenharmony_ci if (!xpc_kdebug_ignore) 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci fallthrough; 118262306a36Sopenharmony_ci case DIE_MCA_MONARCH_LEAVE: 118362306a36Sopenharmony_ci case DIE_INIT_MONARCH_LEAVE: 118462306a36Sopenharmony_ci xpc_arch_ops.online_heartbeat(); 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci#else 118862306a36Sopenharmony_ci struct die_args *die_args = _die_args; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci switch (event) { 119162306a36Sopenharmony_ci case DIE_TRAP: 119262306a36Sopenharmony_ci if (die_args->trapnr == X86_TRAP_DF) 119362306a36Sopenharmony_ci xpc_die_deactivate(); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (((die_args->trapnr == X86_TRAP_MF) || 119662306a36Sopenharmony_ci (die_args->trapnr == X86_TRAP_XF)) && 119762306a36Sopenharmony_ci !user_mode(die_args->regs)) 119862306a36Sopenharmony_ci xpc_die_deactivate(); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci case DIE_INT3: 120262306a36Sopenharmony_ci case DIE_DEBUG: 120362306a36Sopenharmony_ci break; 120462306a36Sopenharmony_ci case DIE_OOPS: 120562306a36Sopenharmony_ci case DIE_GPF: 120662306a36Sopenharmony_ci default: 120762306a36Sopenharmony_ci xpc_die_deactivate(); 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci#endif 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci return NOTIFY_DONE; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic int __init 121562306a36Sopenharmony_cixpc_init(void) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci int ret; 121862306a36Sopenharmony_ci struct task_struct *kthread; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci dev_set_name(xpc_part, "part"); 122162306a36Sopenharmony_ci dev_set_name(xpc_chan, "chan"); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (is_uv_system()) { 122462306a36Sopenharmony_ci ret = xpc_init_uv(); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci } else { 122762306a36Sopenharmony_ci ret = -ENODEV; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (ret != 0) 123162306a36Sopenharmony_ci return ret; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci ret = xpc_setup_partitions(); 123462306a36Sopenharmony_ci if (ret != 0) { 123562306a36Sopenharmony_ci dev_err(xpc_part, "can't get memory for partition structure\n"); 123662306a36Sopenharmony_ci goto out_1; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc); 124062306a36Sopenharmony_ci xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* 124362306a36Sopenharmony_ci * Fill the partition reserved page with the information needed by 124462306a36Sopenharmony_ci * other partitions to discover we are alive and establish initial 124562306a36Sopenharmony_ci * communications. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci ret = xpc_setup_rsvd_page(); 124862306a36Sopenharmony_ci if (ret != 0) { 124962306a36Sopenharmony_ci dev_err(xpc_part, "can't setup our reserved page\n"); 125062306a36Sopenharmony_ci goto out_2; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* add ourselves to the reboot_notifier_list */ 125462306a36Sopenharmony_ci ret = register_reboot_notifier(&xpc_reboot_notifier); 125562306a36Sopenharmony_ci if (ret != 0) 125662306a36Sopenharmony_ci dev_warn(xpc_part, "can't register reboot notifier\n"); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* add ourselves to the die_notifier list */ 125962306a36Sopenharmony_ci ret = register_die_notifier(&xpc_die_notifier); 126062306a36Sopenharmony_ci if (ret != 0) 126162306a36Sopenharmony_ci dev_warn(xpc_part, "can't register die notifier\n"); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* 126462306a36Sopenharmony_ci * The real work-horse behind xpc. This processes incoming 126562306a36Sopenharmony_ci * interrupts and monitors remote heartbeats. 126662306a36Sopenharmony_ci */ 126762306a36Sopenharmony_ci kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME); 126862306a36Sopenharmony_ci if (IS_ERR(kthread)) { 126962306a36Sopenharmony_ci dev_err(xpc_part, "failed while forking hb check thread\n"); 127062306a36Sopenharmony_ci ret = -EBUSY; 127162306a36Sopenharmony_ci goto out_3; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /* 127562306a36Sopenharmony_ci * Startup a thread that will attempt to discover other partitions to 127662306a36Sopenharmony_ci * activate based on info provided by SAL. This new thread is short 127762306a36Sopenharmony_ci * lived and will exit once discovery is complete. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_ci kthread = kthread_run(xpc_initiate_discovery, NULL, 128062306a36Sopenharmony_ci XPC_DISCOVERY_THREAD_NAME); 128162306a36Sopenharmony_ci if (IS_ERR(kthread)) { 128262306a36Sopenharmony_ci dev_err(xpc_part, "failed while forking discovery thread\n"); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* mark this new thread as a non-starter */ 128562306a36Sopenharmony_ci complete(&xpc_discovery_exited); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci xpc_do_exit(xpUnloading); 128862306a36Sopenharmony_ci return -EBUSY; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* set the interface to point at XPC's functions */ 129262306a36Sopenharmony_ci xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, 129362306a36Sopenharmony_ci xpc_initiate_send, xpc_initiate_send_notify, 129462306a36Sopenharmony_ci xpc_initiate_received, xpc_initiate_partid_to_nasids); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* initialization was not successful */ 129962306a36Sopenharmony_ciout_3: 130062306a36Sopenharmony_ci xpc_teardown_rsvd_page(); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci (void)unregister_die_notifier(&xpc_die_notifier); 130362306a36Sopenharmony_ci (void)unregister_reboot_notifier(&xpc_reboot_notifier); 130462306a36Sopenharmony_ciout_2: 130562306a36Sopenharmony_ci if (xpc_sysctl_hb) 130662306a36Sopenharmony_ci unregister_sysctl_table(xpc_sysctl_hb); 130762306a36Sopenharmony_ci if (xpc_sysctl) 130862306a36Sopenharmony_ci unregister_sysctl_table(xpc_sysctl); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci xpc_teardown_partitions(); 131162306a36Sopenharmony_ciout_1: 131262306a36Sopenharmony_ci if (is_uv_system()) 131362306a36Sopenharmony_ci xpc_exit_uv(); 131462306a36Sopenharmony_ci return ret; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cimodule_init(xpc_init); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic void __exit 132062306a36Sopenharmony_cixpc_exit(void) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci xpc_do_exit(xpUnloading); 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cimodule_exit(xpc_exit); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ciMODULE_AUTHOR("Silicon Graphics, Inc."); 132862306a36Sopenharmony_ciMODULE_DESCRIPTION("Cross Partition Communication (XPC) support"); 132962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cimodule_param(xpc_hb_interval, int, 0); 133262306a36Sopenharmony_ciMODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " 133362306a36Sopenharmony_ci "heartbeat increments."); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cimodule_param(xpc_hb_check_interval, int, 0); 133662306a36Sopenharmony_ciMODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " 133762306a36Sopenharmony_ci "heartbeat checks."); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cimodule_param(xpc_disengage_timelimit, int, 0); 134062306a36Sopenharmony_ciMODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait " 134162306a36Sopenharmony_ci "for disengage to complete."); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cimodule_param(xpc_kdebug_ignore, int, 0); 134462306a36Sopenharmony_ciMODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by " 134562306a36Sopenharmony_ci "other partitions when dropping into kdebug."); 1346