/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * (C) Copyright 2020 Hewlett Packard Enterprise Development LP * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. */ /* * Cross Partition Communication (XPC) support - standard version. * * XPC provides a message passing capability that crosses partition * boundaries. This module is made up of two parts: * * partition This part detects the presence/absence of other * partitions. It provides a heartbeat and monitors * the heartbeats of other partitions. * * channel This part manages the channels and sends/receives * messages across them to/from other partitions. * * There are a couple of additional functions residing in XP, which * provide an interface to XPC for its users. * * * Caveats: * * . Currently on sn2, we have no way to determine which nasid an IRQ * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write * followed by an IPI. The amo indicates where data is to be pulled * from, so after the IPI arrives, the remote partition checks the amo * word. The IPI can actually arrive before the amo however, so other * code must periodically check for this case. Also, remote amo * operations do not reliably time out. Thus we do a remote PIO read * solely to know whether the remote partition is down and whether we * should stop sending IPIs to it. This remote PIO read operation is * set up in a special nofault region so SAL knows to ignore (and * cleanup) any errors due to the remote amo write, PIO read, and/or * PIO write operations. * * If/when new hardware solves this IPI problem, we should abandon * the current approach. * */ #include <linux/module.h> #include <linux/slab.h> #include <linux/sysctl.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/kdebug.h> #include <linux/kthread.h> #include "xpc.h" #ifdef CONFIG_X86_64 #include <asm/traps.h> #endif /* define two XPC debug device structures to be used with dev_dbg() et al */ static struct device_driver xpc_dbg_name = …; static struct device xpc_part_dbg_subname = …; static struct device xpc_chan_dbg_subname = …; struct device *xpc_part = …; struct device *xpc_chan = …; static int xpc_kdebug_ignore; /* systune related variables for /proc/sys directories */ static int xpc_hb_interval = …; static int xpc_hb_min_interval = …; static int xpc_hb_max_interval = …; static int xpc_hb_check_interval = …; static int xpc_hb_check_min_interval = …; static int xpc_hb_check_max_interval = …; int xpc_disengage_timelimit = …; static int xpc_disengage_min_timelimit; /* = 0 */ static int xpc_disengage_max_timelimit = …; static struct ctl_table xpc_sys_xpc_hb[] = …; static struct ctl_table xpc_sys_xpc[] = …; static struct ctl_table_header *xpc_sysctl; static struct ctl_table_header *xpc_sysctl_hb; /* non-zero if any remote partition disengage was timed out */ int xpc_disengage_timedout; /* #of activate IRQs received and not yet processed */ int xpc_activate_IRQ_rcvd; DEFINE_SPINLOCK(…); /* IRQ handler notifies this wait queue on receipt of an IRQ */ DECLARE_WAIT_QUEUE_HEAD(…); static unsigned long xpc_hb_check_timeout; static struct timer_list xpc_hb_timer; /* notification that the xpc_hb_checker thread has exited */ static DECLARE_COMPLETION(xpc_hb_checker_exited); /* notification that the xpc_discovery thread has exited */ static DECLARE_COMPLETION(xpc_discovery_exited); static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); static int xpc_system_reboot(struct notifier_block *, unsigned long, void *); static struct notifier_block xpc_reboot_notifier = …; static int xpc_system_die(struct notifier_block *, unsigned long, void *); static struct notifier_block xpc_die_notifier = …; struct xpc_arch_operations xpc_arch_ops; /* * Timer function to enforce the timelimit on the partition disengage. */ static void xpc_timeout_partition_disengage(struct timer_list *t) { … } /* * Timer to produce the heartbeat. The timer structures function is * already set when this is initially called. A tunable is used to * specify when the next timeout should occur. */ static void xpc_hb_beater(struct timer_list *unused) { … } static void xpc_start_hb_beater(void) { … } static void xpc_stop_hb_beater(void) { … } /* * At periodic intervals, scan through all active partitions and ensure * their heartbeat is still active. If not, the partition is deactivated. */ static void xpc_check_remote_hb(void) { … } /* * This thread is responsible for nearly all of the partition * activation/deactivation. */ static int xpc_hb_checker(void *ignore) { … } /* * This thread 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. */ static int xpc_initiate_discovery(void *ignore) { … } /* * The first kthread assigned to a newly activated partition is the one * created by XPC HB with which it calls xpc_activating(). XPC hangs on to * that kthread until the partition is brought down, at which time that kthread * returns back to XPC HB. (The return of that kthread will signify to XPC HB * that XPC has dismantled all communication infrastructure for the associated * partition.) This kthread becomes the channel manager for that partition. * * Each active partition has a channel manager, who, besides connecting and * disconnecting channels, will ensure that each of the partition's connected * channels has the required number of assigned kthreads to get the work done. */ static void xpc_channel_mgr(struct xpc_partition *part) { … } /* * Guarantee that the kzalloc'd memory is cacheline aligned. */ void * xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) { … } /* * Setup the channel structures necessary to support XPartition Communication * between the specified remote partition and the local one. */ static enum xp_retval xpc_setup_ch_structures(struct xpc_partition *part) { … } /* * Teardown the channel structures necessary to support XPartition Communication * between the specified remote partition and the local one. */ static void xpc_teardown_ch_structures(struct xpc_partition *part) { … } /* * When XPC HB determines that a partition has come up, it will create a new * kthread and that kthread will call this function to attempt to set up the * basic infrastructure used for Cross Partition Communication with the newly * upped partition. * * The kthread that was created by XPC HB and which setup the XPC * infrastructure will remain assigned to the partition becoming the channel * manager for that partition until the partition is deactivating, at which * time the kthread will teardown the XPC infrastructure and then exit. */ static int xpc_activating(void *__partid) { … } void xpc_activate_partition(struct xpc_partition *part) { … } void xpc_activate_kthreads(struct xpc_channel *ch, int needed) { … } /* * This function is where XPC's kthreads wait for messages to deliver. */ static void xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch) { … } static int xpc_kthread_start(void *args) { … } /* * For each partition that XPC has established communications with, there is * a minimum of one kernel thread assigned to perform any operation that * may potentially sleep or block (basically the callouts to the asynchronous * functions registered via xpc_connect()). * * Additional kthreads are created and destroyed by XPC as the workload * demands. * * A kthread is assigned to one of the active channels that exists for a given * partition. */ void xpc_create_kthreads(struct xpc_channel *ch, int needed, int ignore_disconnecting) { … } void xpc_disconnect_wait(int ch_number) { … } static int xpc_setup_partitions(void) { … } static void xpc_teardown_partitions(void) { … } static void xpc_do_exit(enum xp_retval reason) { … } /* * This function is called when the system is being rebooted. */ static int xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused) { … } /* Used to only allow one cpu to complete disconnect */ static unsigned int xpc_die_disconnecting; /* * Notify other partitions to deactivate from us by first disengaging from all * references to our memory. */ static void xpc_die_deactivate(void) { … } /* * 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 int xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) { … } static int __init xpc_init(void) { … } module_init(…) …; static void __exit xpc_exit(void) { … } module_exit(xpc_exit); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; module_param(xpc_hb_interval, int, 0); MODULE_PARM_DESC(…) …; module_param(xpc_hb_check_interval, int, 0); MODULE_PARM_DESC(…) …; module_param(xpc_disengage_timelimit, int, 0); MODULE_PARM_DESC(…) …; module_param(xpc_kdebug_ignore, int, 0); MODULE_PARM_DESC(…) …;