aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/ia64/sn/kernel/xpc.h288
-rw-r--r--arch/ia64/sn/kernel/xpc_channel.c216
-rw-r--r--arch/ia64/sn/kernel/xpc_main.c242
-rw-r--r--arch/ia64/sn/kernel/xpc_partition.c304
-rw-r--r--include/asm-ia64/sn/xp.h10
5 files changed, 822 insertions, 238 deletions
diff --git a/arch/ia64/sn/kernel/xpc.h b/arch/ia64/sn/kernel/xpc.h
index d0ee635daf2e..565822ab3d08 100644
--- a/arch/ia64/sn/kernel/xpc.h
+++ b/arch/ia64/sn/kernel/xpc.h
@@ -57,7 +57,7 @@
57#define XPC_NASID_FROM_W_B(_w, _b) (((_w) * 64 + (_b)) * 2) 57#define XPC_NASID_FROM_W_B(_w, _b) (((_w) * 64 + (_b)) * 2)
58 58
59#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */ 59#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */
60#define XPC_HB_CHECK_DEFAULT_TIMEOUT 20 /* check HB every x secs */ 60#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */
61 61
62/* define the process name of HB checker and the CPU it is pinned to */ 62/* define the process name of HB checker and the CPU it is pinned to */
63#define XPC_HB_CHECK_THREAD_NAME "xpc_hb" 63#define XPC_HB_CHECK_THREAD_NAME "xpc_hb"
@@ -67,11 +67,6 @@
67#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery" 67#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery"
68 68
69 69
70#define XPC_HB_ALLOWED(_p, _v) ((_v)->heartbeating_to_mask & (1UL << (_p)))
71#define XPC_ALLOW_HB(_p, _v) (_v)->heartbeating_to_mask |= (1UL << (_p))
72#define XPC_DISALLOW_HB(_p, _v) (_v)->heartbeating_to_mask &= (~(1UL << (_p)))
73
74
75/* 70/*
76 * Reserved Page provided by SAL. 71 * Reserved Page provided by SAL.
77 * 72 *
@@ -88,14 +83,38 @@ struct xpc_rsvd_page {
88 u8 version; 83 u8 version;
89 u8 pad[6]; /* pad to u64 align */ 84 u8 pad[6]; /* pad to u64 align */
90 volatile u64 vars_pa; 85 volatile u64 vars_pa;
86 struct timespec stamp; /* time when reserved page was initialized */
91 u64 part_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned; 87 u64 part_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned;
92 u64 mach_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned; 88 u64 mach_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned;
93}; 89};
94#define XPC_RP_VERSION _XPC_VERSION(1,0) /* version 1.0 of the reserved page */
95 90
96#define XPC_RSVD_PAGE_ALIGNED_SIZE \ 91#define XPC_RSVD_PAGE_ALIGNED_SIZE \
97 (L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page))) 92 (L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)))
98 93
94#define XPC_RP_VERSION _XPC_VERSION(1,1) /* version 1.1 of the reserved page */
95
96#define XPC_SUPPORTS_RP_STAMP(_version) \
97 (_version >= _XPC_VERSION(1,1))
98
99/*
100 * compare stamps - the return value is:
101 *
102 * < 0, if stamp1 < stamp2
103 * = 0, if stamp1 == stamp2
104 * > 0, if stamp1 > stamp2
105 */
106static inline int
107xpc_compare_stamps(struct timespec *stamp1, struct timespec *stamp2)
108{
109 int ret;
110
111
112 if ((ret = stamp1->tv_sec - stamp2->tv_sec) == 0) {
113 ret = stamp1->tv_nsec - stamp2->tv_nsec;
114 }
115 return ret;
116}
117
99 118
100/* 119/*
101 * Define the structures by which XPC variables can be exported to other 120 * Define the structures by which XPC variables can be exported to other
@@ -121,12 +140,61 @@ struct xpc_vars {
121 u64 vars_part_pa; 140 u64 vars_part_pa;
122 u64 amos_page_pa; /* paddr of page of AMOs from MSPEC driver */ 141 u64 amos_page_pa; /* paddr of page of AMOs from MSPEC driver */
123 AMO_t *amos_page; /* vaddr of page of AMOs from MSPEC driver */ 142 AMO_t *amos_page; /* vaddr of page of AMOs from MSPEC driver */
124 AMO_t *act_amos; /* pointer to the first activation AMO */
125}; 143};
126#define XPC_V_VERSION _XPC_VERSION(3,0) /* version 3.0 of the cross vars */
127 144
128#define XPC_VARS_ALIGNED_SIZE (L1_CACHE_ALIGN(sizeof(struct xpc_vars))) 145#define XPC_VARS_ALIGNED_SIZE (L1_CACHE_ALIGN(sizeof(struct xpc_vars)))
129 146
147#define XPC_V_VERSION _XPC_VERSION(3,1) /* version 3.1 of the cross vars */
148
149#define XPC_SUPPORTS_DISENGAGE_REQUEST(_version) \
150 (_version >= _XPC_VERSION(3,1))
151
152
153static inline int
154xpc_hb_allowed(partid_t partid, struct xpc_vars *vars)
155{
156 return ((vars->heartbeating_to_mask & (1UL << partid)) != 0);
157}
158
159static inline void
160xpc_allow_hb(partid_t partid, struct xpc_vars *vars)
161{
162 u64 old_mask, new_mask;
163
164 do {
165 old_mask = vars->heartbeating_to_mask;
166 new_mask = (old_mask | (1UL << partid));
167 } while (cmpxchg(&vars->heartbeating_to_mask, old_mask, new_mask) !=
168 old_mask);
169}
170
171static inline void
172xpc_disallow_hb(partid_t partid, struct xpc_vars *vars)
173{
174 u64 old_mask, new_mask;
175
176 do {
177 old_mask = vars->heartbeating_to_mask;
178 new_mask = (old_mask & ~(1UL << partid));
179 } while (cmpxchg(&vars->heartbeating_to_mask, old_mask, new_mask) !=
180 old_mask);
181}
182
183
184/*
185 * The AMOs page consists of a number of AMO variables which are divided into
186 * four groups, The first two groups are used to identify an IRQ's sender.
187 * These two groups consist of 64 and 16 AMO variables respectively. The last
188 * two groups, consisting of just one AMO variable each, are used to identify
189 * the remote partitions that are currently engaged (from the viewpoint of
190 * the XPC running on the remote partition).
191 */
192#define XPC_NOTIFY_IRQ_AMOS 0
193#define XPC_ACTIVATE_IRQ_AMOS (XPC_NOTIFY_IRQ_AMOS + XP_MAX_PARTITIONS)
194#define XPC_ENGAGED_PARTITIONS_AMO (XPC_ACTIVATE_IRQ_AMOS + XP_NASID_MASK_WORDS)
195#define XPC_DISENGAGE_REQUEST_AMO (XPC_ENGAGED_PARTITIONS_AMO + 1)
196
197
130/* 198/*
131 * The following structure describes the per partition specific variables. 199 * The following structure describes the per partition specific variables.
132 * 200 *
@@ -358,7 +426,7 @@ struct xpc_channel {
358 void *key; /* pointer to user's key */ 426 void *key; /* pointer to user's key */
359 427
360 struct semaphore msg_to_pull_sema; /* next msg to pull serialization */ 428 struct semaphore msg_to_pull_sema; /* next msg to pull serialization */
361 struct semaphore teardown_sema; /* wait for teardown completion */ 429 struct semaphore wdisconnect_sema; /* wait for channel disconnect */
362 430
363 struct xpc_openclose_args *local_openclose_args; /* args passed on */ 431 struct xpc_openclose_args *local_openclose_args; /* args passed on */
364 /* opening or closing of channel */ 432 /* opening or closing of channel */
@@ -410,6 +478,7 @@ struct xpc_channel {
410 478
411#define XPC_C_DISCONNECTED 0x00002000 /* channel is disconnected */ 479#define XPC_C_DISCONNECTED 0x00002000 /* channel is disconnected */
412#define XPC_C_DISCONNECTING 0x00004000 /* channel is being disconnected */ 480#define XPC_C_DISCONNECTING 0x00004000 /* channel is being disconnected */
481#define XPC_C_WDISCONNECT 0x00008000 /* waiting for channel disconnect */
413 482
414 483
415 484
@@ -422,6 +491,8 @@ struct xpc_partition {
422 491
423 /* XPC HB infrastructure */ 492 /* XPC HB infrastructure */
424 493
494 u8 remote_rp_version; /* version# of partition's rsvd pg */
495 struct timespec remote_rp_stamp;/* time when rsvd pg was initialized */
425 u64 remote_rp_pa; /* phys addr of partition's rsvd pg */ 496 u64 remote_rp_pa; /* phys addr of partition's rsvd pg */
426 u64 remote_vars_pa; /* phys addr of partition's vars */ 497 u64 remote_vars_pa; /* phys addr of partition's vars */
427 u64 remote_vars_part_pa; /* phys addr of partition's vars part */ 498 u64 remote_vars_part_pa; /* phys addr of partition's vars part */
@@ -432,10 +503,14 @@ struct xpc_partition {
432 u32 act_IRQ_rcvd; /* IRQs since activation */ 503 u32 act_IRQ_rcvd; /* IRQs since activation */
433 spinlock_t act_lock; /* protect updating of act_state */ 504 spinlock_t act_lock; /* protect updating of act_state */
434 u8 act_state; /* from XPC HB viewpoint */ 505 u8 act_state; /* from XPC HB viewpoint */
506 u8 remote_vars_version; /* version# of partition's vars */
435 enum xpc_retval reason; /* reason partition is deactivating */ 507 enum xpc_retval reason; /* reason partition is deactivating */
436 int reason_line; /* line# deactivation initiated from */ 508 int reason_line; /* line# deactivation initiated from */
437 int reactivate_nasid; /* nasid in partition to reactivate */ 509 int reactivate_nasid; /* nasid in partition to reactivate */
438 510
511 unsigned long disengage_request_timeout; /* timeout in XPC_TICKS */
512 struct timer_list disengage_request_timer;
513
439 514
440 /* XPC infrastructure referencing and teardown control */ 515 /* XPC infrastructure referencing and teardown control */
441 516
@@ -454,6 +529,7 @@ struct xpc_partition {
454 529
455 u8 nchannels; /* #of defined channels supported */ 530 u8 nchannels; /* #of defined channels supported */
456 atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */ 531 atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */
532 atomic_t nchannels_engaged;/* #of channels engaged with remote part */
457 struct xpc_channel *channels;/* array of channel structures */ 533 struct xpc_channel *channels;/* array of channel structures */
458 534
459 void *local_GPs_base; /* base address of kmalloc'd space */ 535 void *local_GPs_base; /* base address of kmalloc'd space */
@@ -518,6 +594,7 @@ struct xpc_partition {
518#define XPC_P_TORNDOWN 0x03 /* infrastructure is torndown */ 594#define XPC_P_TORNDOWN 0x03 /* infrastructure is torndown */
519 595
520 596
597
521/* 598/*
522 * struct xpc_partition IPI_timer #of seconds to wait before checking for 599 * struct xpc_partition IPI_timer #of seconds to wait before checking for
523 * dropped IPIs. These occur whenever an IPI amo write doesn't complete until 600 * dropped IPIs. These occur whenever an IPI amo write doesn't complete until
@@ -526,6 +603,13 @@ struct xpc_partition {
526#define XPC_P_DROPPED_IPI_WAIT (0.25 * HZ) 603#define XPC_P_DROPPED_IPI_WAIT (0.25 * HZ)
527 604
528 605
606/* number of seconds to wait for other partitions to disengage */
607#define XPC_DISENGAGE_REQUEST_TIMELIMIT 90
608
609/* interval in seconds to print 'waiting disengagement' messages */
610#define XPC_DISENGAGE_PRINTMSG_INTERVAL 10
611
612
529#define XPC_PARTID(_p) ((partid_t) ((_p) - &xpc_partitions[0])) 613#define XPC_PARTID(_p) ((partid_t) ((_p) - &xpc_partitions[0]))
530 614
531 615
@@ -550,8 +634,6 @@ extern void xpc_activate_partition(struct xpc_partition *);
550 634
551/* found in xpc_partition.c */ 635/* found in xpc_partition.c */
552extern int xpc_exiting; 636extern int xpc_exiting;
553extern int xpc_hb_interval;
554extern int xpc_hb_check_interval;
555extern struct xpc_vars *xpc_vars; 637extern struct xpc_vars *xpc_vars;
556extern struct xpc_rsvd_page *xpc_rsvd_page; 638extern struct xpc_rsvd_page *xpc_rsvd_page;
557extern struct xpc_vars_part *xpc_vars_part; 639extern struct xpc_vars_part *xpc_vars_part;
@@ -561,6 +643,7 @@ extern struct xpc_rsvd_page *xpc_rsvd_page_init(void);
561extern void xpc_allow_IPI_ops(void); 643extern void xpc_allow_IPI_ops(void);
562extern void xpc_restrict_IPI_ops(void); 644extern void xpc_restrict_IPI_ops(void);
563extern int xpc_identify_act_IRQ_sender(void); 645extern int xpc_identify_act_IRQ_sender(void);
646extern int xpc_partition_disengaged(struct xpc_partition *);
564extern enum xpc_retval xpc_mark_partition_active(struct xpc_partition *); 647extern enum xpc_retval xpc_mark_partition_active(struct xpc_partition *);
565extern void xpc_mark_partition_inactive(struct xpc_partition *); 648extern void xpc_mark_partition_inactive(struct xpc_partition *);
566extern void xpc_discovery(void); 649extern void xpc_discovery(void);
@@ -585,8 +668,8 @@ extern void xpc_connected_callout(struct xpc_channel *);
585extern void xpc_deliver_msg(struct xpc_channel *); 668extern void xpc_deliver_msg(struct xpc_channel *);
586extern void xpc_disconnect_channel(const int, struct xpc_channel *, 669extern void xpc_disconnect_channel(const int, struct xpc_channel *,
587 enum xpc_retval, unsigned long *); 670 enum xpc_retval, unsigned long *);
588extern void xpc_disconnected_callout(struct xpc_channel *); 671extern void xpc_disconnecting_callout(struct xpc_channel *);
589extern void xpc_partition_down(struct xpc_partition *, enum xpc_retval); 672extern void xpc_partition_going_down(struct xpc_partition *, enum xpc_retval);
590extern void xpc_teardown_infrastructure(struct xpc_partition *); 673extern void xpc_teardown_infrastructure(struct xpc_partition *);
591 674
592 675
@@ -674,6 +757,157 @@ xpc_part_ref(struct xpc_partition *part)
674 757
675 758
676/* 759/*
760 * This next set of inlines are used to keep track of when a partition is
761 * potentially engaged in accessing memory belonging to another partition.
762 */
763
764static inline void
765xpc_mark_partition_engaged(struct xpc_partition *part)
766{
767 unsigned long irq_flags;
768 AMO_t *amo = (AMO_t *) __va(part->remote_amos_page_pa +
769 (XPC_ENGAGED_PARTITIONS_AMO * sizeof(AMO_t)));
770
771
772 local_irq_save(irq_flags);
773
774 /* set bit corresponding to our partid in remote partition's AMO */
775 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_OR,
776 (1UL << sn_partition_id));
777 /*
778 * We must always use the nofault function regardless of whether we
779 * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we
780 * didn't, we'd never know that the other partition is down and would
781 * keep sending IPIs and AMOs to it until the heartbeat times out.
782 */
783 (void) xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->
784 variable), xp_nofault_PIOR_target));
785
786 local_irq_restore(irq_flags);
787}
788
789static inline void
790xpc_mark_partition_disengaged(struct xpc_partition *part)
791{
792 unsigned long irq_flags;
793 AMO_t *amo = (AMO_t *) __va(part->remote_amos_page_pa +
794 (XPC_ENGAGED_PARTITIONS_AMO * sizeof(AMO_t)));
795
796
797 local_irq_save(irq_flags);
798
799 /* clear bit corresponding to our partid in remote partition's AMO */
800 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_AND,
801 ~(1UL << sn_partition_id));
802 /*
803 * We must always use the nofault function regardless of whether we
804 * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we
805 * didn't, we'd never know that the other partition is down and would
806 * keep sending IPIs and AMOs to it until the heartbeat times out.
807 */
808 (void) xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->
809 variable), xp_nofault_PIOR_target));
810
811 local_irq_restore(irq_flags);
812}
813
814static inline void
815xpc_request_partition_disengage(struct xpc_partition *part)
816{
817 unsigned long irq_flags;
818 AMO_t *amo = (AMO_t *) __va(part->remote_amos_page_pa +
819 (XPC_DISENGAGE_REQUEST_AMO * sizeof(AMO_t)));
820
821
822 local_irq_save(irq_flags);
823
824 /* set bit corresponding to our partid in remote partition's AMO */
825 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_OR,
826 (1UL << sn_partition_id));
827 /*
828 * We must always use the nofault function regardless of whether we
829 * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we
830 * didn't, we'd never know that the other partition is down and would
831 * keep sending IPIs and AMOs to it until the heartbeat times out.
832 */
833 (void) xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->
834 variable), xp_nofault_PIOR_target));
835
836 local_irq_restore(irq_flags);
837}
838
839static inline void
840xpc_cancel_partition_disengage_request(struct xpc_partition *part)
841{
842 unsigned long irq_flags;
843 AMO_t *amo = (AMO_t *) __va(part->remote_amos_page_pa +
844 (XPC_DISENGAGE_REQUEST_AMO * sizeof(AMO_t)));
845
846
847 local_irq_save(irq_flags);
848
849 /* clear bit corresponding to our partid in remote partition's AMO */
850 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_AND,
851 ~(1UL << sn_partition_id));
852 /*
853 * We must always use the nofault function regardless of whether we
854 * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we
855 * didn't, we'd never know that the other partition is down and would
856 * keep sending IPIs and AMOs to it until the heartbeat times out.
857 */
858 (void) xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->
859 variable), xp_nofault_PIOR_target));
860
861 local_irq_restore(irq_flags);
862}
863
864static inline u64
865xpc_partition_engaged(u64 partid_mask)
866{
867 AMO_t *amo = xpc_vars->amos_page + XPC_ENGAGED_PARTITIONS_AMO;
868
869
870 /* return our partition's AMO variable ANDed with partid_mask */
871 return (FETCHOP_LOAD_OP(TO_AMO((u64) &amo->variable), FETCHOP_LOAD) &
872 partid_mask);
873}
874
875static inline u64
876xpc_partition_disengage_requested(u64 partid_mask)
877{
878 AMO_t *amo = xpc_vars->amos_page + XPC_DISENGAGE_REQUEST_AMO;
879
880
881 /* return our partition's AMO variable ANDed with partid_mask */
882 return (FETCHOP_LOAD_OP(TO_AMO((u64) &amo->variable), FETCHOP_LOAD) &
883 partid_mask);
884}
885
886static inline void
887xpc_clear_partition_engaged(u64 partid_mask)
888{
889 AMO_t *amo = xpc_vars->amos_page + XPC_ENGAGED_PARTITIONS_AMO;
890
891
892 /* clear bit(s) based on partid_mask in our partition's AMO */
893 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_AND,
894 ~partid_mask);
895}
896
897static inline void
898xpc_clear_partition_disengage_request(u64 partid_mask)
899{
900 AMO_t *amo = xpc_vars->amos_page + XPC_DISENGAGE_REQUEST_AMO;
901
902
903 /* clear bit(s) based on partid_mask in our partition's AMO */
904 FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_AND,
905 ~partid_mask);
906}
907
908
909
910/*
677 * The following set of macros and inlines are used for the sending and 911 * The following set of macros and inlines are used for the sending and
678 * receiving of IPIs (also known as IRQs). There are two flavors of IPIs, 912 * receiving of IPIs (also known as IRQs). There are two flavors of IPIs,
679 * one that is associated with partition activity (SGI_XPC_ACTIVATE) and 913 * one that is associated with partition activity (SGI_XPC_ACTIVATE) and
@@ -722,13 +956,13 @@ xpc_IPI_send(AMO_t *amo, u64 flag, int nasid, int phys_cpuid, int vector)
722 * Flag the appropriate AMO variable and send an IPI to the specified node. 956 * Flag the appropriate AMO variable and send an IPI to the specified node.
723 */ 957 */
724static inline void 958static inline void
725xpc_activate_IRQ_send(u64 amos_page, int from_nasid, int to_nasid, 959xpc_activate_IRQ_send(u64 amos_page_pa, int from_nasid, int to_nasid,
726 int to_phys_cpuid) 960 int to_phys_cpuid)
727{ 961{
728 int w_index = XPC_NASID_W_INDEX(from_nasid); 962 int w_index = XPC_NASID_W_INDEX(from_nasid);
729 int b_index = XPC_NASID_B_INDEX(from_nasid); 963 int b_index = XPC_NASID_B_INDEX(from_nasid);
730 AMO_t *amos = (AMO_t *) __va(amos_page + 964 AMO_t *amos = (AMO_t *) __va(amos_page_pa +
731 (XP_MAX_PARTITIONS * sizeof(AMO_t))); 965 (XPC_ACTIVATE_IRQ_AMOS * sizeof(AMO_t)));
732 966
733 967
734 (void) xpc_IPI_send(&amos[w_index], (1UL << b_index), to_nasid, 968 (void) xpc_IPI_send(&amos[w_index], (1UL << b_index), to_nasid,
@@ -756,6 +990,13 @@ xpc_IPI_send_reactivate(struct xpc_partition *part)
756 xpc_vars->act_nasid, xpc_vars->act_phys_cpuid); 990 xpc_vars->act_nasid, xpc_vars->act_phys_cpuid);
757} 991}
758 992
993static inline void
994xpc_IPI_send_disengage(struct xpc_partition *part)
995{
996 xpc_activate_IRQ_send(part->remote_amos_page_pa, cnodeid_to_nasid(0),
997 part->remote_act_nasid, part->remote_act_phys_cpuid);
998}
999
759 1000
760/* 1001/*
761 * IPIs associated with SGI_XPC_NOTIFY IRQ. 1002 * IPIs associated with SGI_XPC_NOTIFY IRQ.
@@ -903,17 +1144,18 @@ xpc_IPI_send_local_msgrequest(struct xpc_channel *ch)
903 * cacheable mapping for the entire region. This will prevent speculative 1144 * cacheable mapping for the entire region. This will prevent speculative
904 * reading of cached copies of our lines from being issued which will cause 1145 * reading of cached copies of our lines from being issued which will cause
905 * a PI FSB Protocol error to be generated by the SHUB. For XPC, we need 64 1146 * a PI FSB Protocol error to be generated by the SHUB. For XPC, we need 64
906 * (XP_MAX_PARTITIONS) AMO variables for message notification (xpc_main.c) 1147 * (XP_MAX_PARTITIONS) AMO variables for message notification and an
907 * and an additional 16 AMO variables for partition activation (xpc_hb.c). 1148 * additional 16 (XP_NASID_MASK_WORDS) AMO variables for partition activation
1149 * and 2 AMO variables for partition deactivation.
908 */ 1150 */
909static inline AMO_t * 1151static inline AMO_t *
910xpc_IPI_init(partid_t partid) 1152xpc_IPI_init(int index)
911{ 1153{
912 AMO_t *part_amo = xpc_vars->amos_page + partid; 1154 AMO_t *amo = xpc_vars->amos_page + index;
913 1155
914 1156
915 xpc_IPI_receive(part_amo); 1157 (void) xpc_IPI_receive(amo); /* clear AMO variable */
916 return part_amo; 1158 return amo;
917} 1159}
918 1160
919 1161
diff --git a/arch/ia64/sn/kernel/xpc_channel.c b/arch/ia64/sn/kernel/xpc_channel.c
index 94698bea7be0..195ac1b8e262 100644
--- a/arch/ia64/sn/kernel/xpc_channel.c
+++ b/arch/ia64/sn/kernel/xpc_channel.c
@@ -57,6 +57,7 @@ xpc_initialize_channels(struct xpc_partition *part, partid_t partid)
57 57
58 spin_lock_init(&ch->lock); 58 spin_lock_init(&ch->lock);
59 sema_init(&ch->msg_to_pull_sema, 1); /* mutex */ 59 sema_init(&ch->msg_to_pull_sema, 1); /* mutex */
60 sema_init(&ch->wdisconnect_sema, 0); /* event wait */
60 61
61 atomic_set(&ch->n_on_msg_allocate_wq, 0); 62 atomic_set(&ch->n_on_msg_allocate_wq, 0);
62 init_waitqueue_head(&ch->msg_allocate_wq); 63 init_waitqueue_head(&ch->msg_allocate_wq);
@@ -166,6 +167,7 @@ xpc_setup_infrastructure(struct xpc_partition *part)
166 xpc_initialize_channels(part, partid); 167 xpc_initialize_channels(part, partid);
167 168
168 atomic_set(&part->nchannels_active, 0); 169 atomic_set(&part->nchannels_active, 0);
170 atomic_set(&part->nchannels_engaged, 0);
169 171
170 172
171 /* local_IPI_amo were set to 0 by an earlier memset() */ 173 /* local_IPI_amo were set to 0 by an earlier memset() */
@@ -555,8 +557,6 @@ xpc_allocate_msgqueues(struct xpc_channel *ch)
555 sema_init(&ch->notify_queue[i].sema, 0); 557 sema_init(&ch->notify_queue[i].sema, 0);
556 } 558 }
557 559
558 sema_init(&ch->teardown_sema, 0); /* event wait */
559
560 spin_lock_irqsave(&ch->lock, irq_flags); 560 spin_lock_irqsave(&ch->lock, irq_flags);
561 ch->flags |= XPC_C_SETUP; 561 ch->flags |= XPC_C_SETUP;
562 spin_unlock_irqrestore(&ch->lock, irq_flags); 562 spin_unlock_irqrestore(&ch->lock, irq_flags);
@@ -626,6 +626,55 @@ xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
626 626
627 627
628/* 628/*
629 * Notify those who wanted to be notified upon delivery of their message.
630 */
631static void
632xpc_notify_senders(struct xpc_channel *ch, enum xpc_retval reason, s64 put)
633{
634 struct xpc_notify *notify;
635 u8 notify_type;
636 s64 get = ch->w_remote_GP.get - 1;
637
638
639 while (++get < put && atomic_read(&ch->n_to_notify) > 0) {
640
641 notify = &ch->notify_queue[get % ch->local_nentries];
642
643 /*
644 * See if the notify entry indicates it was associated with
645 * a message who's sender wants to be notified. It is possible
646 * that it is, but someone else is doing or has done the
647 * notification.
648 */
649 notify_type = notify->type;
650 if (notify_type == 0 ||
651 cmpxchg(&notify->type, notify_type, 0) !=
652 notify_type) {
653 continue;
654 }
655
656 DBUG_ON(notify_type != XPC_N_CALL);
657
658 atomic_dec(&ch->n_to_notify);
659
660 if (notify->func != NULL) {
661 dev_dbg(xpc_chan, "notify->func() called, notify=0x%p, "
662 "msg_number=%ld, partid=%d, channel=%d\n",
663 (void *) notify, get, ch->partid, ch->number);
664
665 notify->func(reason, ch->partid, ch->number,
666 notify->key);
667
668 dev_dbg(xpc_chan, "notify->func() returned, "
669 "notify=0x%p, msg_number=%ld, partid=%d, "
670 "channel=%d\n", (void *) notify, get,
671 ch->partid, ch->number);
672 }
673 }
674}
675
676
677/*
629 * Free up message queues and other stuff that were allocated for the specified 678 * Free up message queues and other stuff that were allocated for the specified
630 * channel. 679 * channel.
631 * 680 *
@@ -669,9 +718,6 @@ xpc_free_msgqueues(struct xpc_channel *ch)
669 ch->remote_msgqueue = NULL; 718 ch->remote_msgqueue = NULL;
670 kfree(ch->notify_queue); 719 kfree(ch->notify_queue);
671 ch->notify_queue = NULL; 720 ch->notify_queue = NULL;
672
673 /* in case someone is waiting for the teardown to complete */
674 up(&ch->teardown_sema);
675 } 721 }
676} 722}
677 723
@@ -683,7 +729,7 @@ static void
683xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) 729xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
684{ 730{
685 struct xpc_partition *part = &xpc_partitions[ch->partid]; 731 struct xpc_partition *part = &xpc_partitions[ch->partid];
686 u32 ch_flags = ch->flags; 732 u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);
687 733
688 734
689 DBUG_ON(!spin_is_locked(&ch->lock)); 735 DBUG_ON(!spin_is_locked(&ch->lock));
@@ -701,12 +747,13 @@ xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
701 } 747 }
702 DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0); 748 DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
703 749
704 /* it's now safe to free the channel's message queues */ 750 if (part->act_state == XPC_P_DEACTIVATING) {
705 751 /* can't proceed until the other side disengages from us */
706 xpc_free_msgqueues(ch); 752 if (xpc_partition_engaged(1UL << ch->partid)) {
707 DBUG_ON(ch->flags & XPC_C_SETUP); 753 return;
754 }
708 755
709 if (part->act_state != XPC_P_DEACTIVATING) { 756 } else {
710 757
711 /* as long as the other side is up do the full protocol */ 758 /* as long as the other side is up do the full protocol */
712 759
@@ -724,16 +771,33 @@ xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
724 } 771 }
725 } 772 }
726 773
774 /* wake those waiting for notify completion */
775 if (atomic_read(&ch->n_to_notify) > 0) {
776 /* >>> we do callout while holding ch->lock */
777 xpc_notify_senders(ch, ch->reason, ch->w_local_GP.put);
778 }
779
727 /* both sides are disconnected now */ 780 /* both sides are disconnected now */
728 781
729 ch->flags = XPC_C_DISCONNECTED; /* clear all flags, but this one */ 782 /* it's now safe to free the channel's message queues */
783 xpc_free_msgqueues(ch);
784
785 /* mark disconnected, clear all other flags except XPC_C_WDISCONNECT */
786 ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
730 787
731 atomic_dec(&part->nchannels_active); 788 atomic_dec(&part->nchannels_active);
732 789
733 if (ch_flags & XPC_C_WASCONNECTED) { 790 if (channel_was_connected) {
734 dev_info(xpc_chan, "channel %d to partition %d disconnected, " 791 dev_info(xpc_chan, "channel %d to partition %d disconnected, "
735 "reason=%d\n", ch->number, ch->partid, ch->reason); 792 "reason=%d\n", ch->number, ch->partid, ch->reason);
736 } 793 }
794
795 /* wake the thread that is waiting for this channel to disconnect */
796 if (ch->flags & XPC_C_WDISCONNECT) {
797 spin_unlock_irqrestore(&ch->lock, *irq_flags);
798 up(&ch->wdisconnect_sema);
799 spin_lock_irqsave(&ch->lock, *irq_flags);
800 }
737} 801}
738 802
739 803
@@ -764,7 +828,7 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
764 /* 828 /*
765 * If RCLOSEREQUEST is set, we're probably waiting for 829 * If RCLOSEREQUEST is set, we're probably waiting for
766 * RCLOSEREPLY. We should find it and a ROPENREQUEST packed 830 * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
767 * with this RCLOSEQREUQEST in the IPI_flags. 831 * with this RCLOSEREQUEST in the IPI_flags.
768 */ 832 */
769 833
770 if (ch->flags & XPC_C_RCLOSEREQUEST) { 834 if (ch->flags & XPC_C_RCLOSEREQUEST) {
@@ -852,7 +916,7 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
852 "channel=%d\n", args->msg_size, args->local_nentries, 916 "channel=%d\n", args->msg_size, args->local_nentries,
853 ch->partid, ch->number); 917 ch->partid, ch->number);
854 918
855 if ((ch->flags & XPC_C_DISCONNECTING) || 919 if ((ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) ||
856 part->act_state == XPC_P_DEACTIVATING) { 920 part->act_state == XPC_P_DEACTIVATING) {
857 spin_unlock_irqrestore(&ch->lock, irq_flags); 921 spin_unlock_irqrestore(&ch->lock, irq_flags);
858 return; 922 return;
@@ -1040,55 +1104,6 @@ xpc_connect_channel(struct xpc_channel *ch)
1040 1104
1041 1105
1042/* 1106/*
1043 * Notify those who wanted to be notified upon delivery of their message.
1044 */
1045static void
1046xpc_notify_senders(struct xpc_channel *ch, enum xpc_retval reason, s64 put)
1047{
1048 struct xpc_notify *notify;
1049 u8 notify_type;
1050 s64 get = ch->w_remote_GP.get - 1;
1051
1052
1053 while (++get < put && atomic_read(&ch->n_to_notify) > 0) {
1054
1055 notify = &ch->notify_queue[get % ch->local_nentries];
1056
1057 /*
1058 * See if the notify entry indicates it was associated with
1059 * a message who's sender wants to be notified. It is possible
1060 * that it is, but someone else is doing or has done the
1061 * notification.
1062 */
1063 notify_type = notify->type;
1064 if (notify_type == 0 ||
1065 cmpxchg(&notify->type, notify_type, 0) !=
1066 notify_type) {
1067 continue;
1068 }
1069
1070 DBUG_ON(notify_type != XPC_N_CALL);
1071
1072 atomic_dec(&ch->n_to_notify);
1073
1074 if (notify->func != NULL) {
1075 dev_dbg(xpc_chan, "notify->func() called, notify=0x%p, "
1076 "msg_number=%ld, partid=%d, channel=%d\n",
1077 (void *) notify, get, ch->partid, ch->number);
1078
1079 notify->func(reason, ch->partid, ch->number,
1080 notify->key);
1081
1082 dev_dbg(xpc_chan, "notify->func() returned, "
1083 "notify=0x%p, msg_number=%ld, partid=%d, "
1084 "channel=%d\n", (void *) notify, get,
1085 ch->partid, ch->number);
1086 }
1087 }
1088}
1089
1090
1091/*
1092 * Clear some of the msg flags in the local message queue. 1107 * Clear some of the msg flags in the local message queue.
1093 */ 1108 */
1094static inline void 1109static inline void
@@ -1240,6 +1255,7 @@ xpc_process_channel_activity(struct xpc_partition *part)
1240 u64 IPI_amo, IPI_flags; 1255 u64 IPI_amo, IPI_flags;
1241 struct xpc_channel *ch; 1256 struct xpc_channel *ch;
1242 int ch_number; 1257 int ch_number;
1258 u32 ch_flags;
1243 1259
1244 1260
1245 IPI_amo = xpc_get_IPI_flags(part); 1261 IPI_amo = xpc_get_IPI_flags(part);
@@ -1266,8 +1282,9 @@ xpc_process_channel_activity(struct xpc_partition *part)
1266 xpc_process_openclose_IPI(part, ch_number, IPI_flags); 1282 xpc_process_openclose_IPI(part, ch_number, IPI_flags);
1267 } 1283 }
1268 1284
1285 ch_flags = ch->flags; /* need an atomic snapshot of flags */
1269 1286
1270 if (ch->flags & XPC_C_DISCONNECTING) { 1287 if (ch_flags & XPC_C_DISCONNECTING) {
1271 spin_lock_irqsave(&ch->lock, irq_flags); 1288 spin_lock_irqsave(&ch->lock, irq_flags);
1272 xpc_process_disconnect(ch, &irq_flags); 1289 xpc_process_disconnect(ch, &irq_flags);
1273 spin_unlock_irqrestore(&ch->lock, irq_flags); 1290 spin_unlock_irqrestore(&ch->lock, irq_flags);
@@ -1278,9 +1295,9 @@ xpc_process_channel_activity(struct xpc_partition *part)
1278 continue; 1295 continue;
1279 } 1296 }
1280 1297
1281 if (!(ch->flags & XPC_C_CONNECTED)) { 1298 if (!(ch_flags & XPC_C_CONNECTED)) {
1282 if (!(ch->flags & XPC_C_OPENREQUEST)) { 1299 if (!(ch_flags & XPC_C_OPENREQUEST)) {
1283 DBUG_ON(ch->flags & XPC_C_SETUP); 1300 DBUG_ON(ch_flags & XPC_C_SETUP);
1284 (void) xpc_connect_channel(ch); 1301 (void) xpc_connect_channel(ch);
1285 } else { 1302 } else {
1286 spin_lock_irqsave(&ch->lock, irq_flags); 1303 spin_lock_irqsave(&ch->lock, irq_flags);
@@ -1305,8 +1322,8 @@ xpc_process_channel_activity(struct xpc_partition *part)
1305 1322
1306 1323
1307/* 1324/*
1308 * XPC's heartbeat code calls this function to inform XPC that a partition has 1325 * XPC's heartbeat code calls this function to inform XPC that a partition is
1309 * gone down. XPC responds by tearing down the XPartition Communication 1326 * going down. XPC responds by tearing down the XPartition Communication
1310 * infrastructure used for the just downed partition. 1327 * infrastructure used for the just downed partition.
1311 * 1328 *
1312 * XPC's heartbeat code will never call this function and xpc_partition_up() 1329 * XPC's heartbeat code will never call this function and xpc_partition_up()
@@ -1314,7 +1331,7 @@ xpc_process_channel_activity(struct xpc_partition *part)
1314 * at the same time. 1331 * at the same time.
1315 */ 1332 */
1316void 1333void
1317xpc_partition_down(struct xpc_partition *part, enum xpc_retval reason) 1334xpc_partition_going_down(struct xpc_partition *part, enum xpc_retval reason)
1318{ 1335{
1319 unsigned long irq_flags; 1336 unsigned long irq_flags;
1320 int ch_number; 1337 int ch_number;
@@ -1330,12 +1347,11 @@ xpc_partition_down(struct xpc_partition *part, enum xpc_retval reason)
1330 } 1347 }
1331 1348
1332 1349
1333 /* disconnect all channels associated with the downed partition */ 1350 /* disconnect channels associated with the partition going down */
1334 1351
1335 for (ch_number = 0; ch_number < part->nchannels; ch_number++) { 1352 for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
1336 ch = &part->channels[ch_number]; 1353 ch = &part->channels[ch_number];
1337 1354
1338
1339 xpc_msgqueue_ref(ch); 1355 xpc_msgqueue_ref(ch);
1340 spin_lock_irqsave(&ch->lock, irq_flags); 1356 spin_lock_irqsave(&ch->lock, irq_flags);
1341 1357
@@ -1370,6 +1386,7 @@ xpc_teardown_infrastructure(struct xpc_partition *part)
1370 * this partition. 1386 * this partition.
1371 */ 1387 */
1372 1388
1389 DBUG_ON(atomic_read(&part->nchannels_engaged) != 0);
1373 DBUG_ON(atomic_read(&part->nchannels_active) != 0); 1390 DBUG_ON(atomic_read(&part->nchannels_active) != 0);
1374 DBUG_ON(part->setup_state != XPC_P_SETUP); 1391 DBUG_ON(part->setup_state != XPC_P_SETUP);
1375 part->setup_state = XPC_P_WTEARDOWN; 1392 part->setup_state = XPC_P_WTEARDOWN;
@@ -1506,8 +1523,12 @@ xpc_initiate_disconnect(int ch_number)
1506 1523
1507 spin_lock_irqsave(&ch->lock, irq_flags); 1524 spin_lock_irqsave(&ch->lock, irq_flags);
1508 1525
1509 XPC_DISCONNECT_CHANNEL(ch, xpcUnregistering, 1526 if (!(ch->flags & XPC_C_DISCONNECTED)) {
1527 ch->flags |= XPC_C_WDISCONNECT;
1528
1529 XPC_DISCONNECT_CHANNEL(ch, xpcUnregistering,
1510 &irq_flags); 1530 &irq_flags);
1531 }
1511 1532
1512 spin_unlock_irqrestore(&ch->lock, irq_flags); 1533 spin_unlock_irqrestore(&ch->lock, irq_flags);
1513 1534
@@ -1523,8 +1544,9 @@ xpc_initiate_disconnect(int ch_number)
1523/* 1544/*
1524 * To disconnect a channel, and reflect it back to all who may be waiting. 1545 * To disconnect a channel, and reflect it back to all who may be waiting.
1525 * 1546 *
1526 * >>> An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by 1547 * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
1527 * >>> xpc_free_msgqueues(). 1548 * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
1549 * xpc_disconnect_wait().
1528 * 1550 *
1529 * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. 1551 * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
1530 */ 1552 */
@@ -1532,7 +1554,7 @@ void
1532xpc_disconnect_channel(const int line, struct xpc_channel *ch, 1554xpc_disconnect_channel(const int line, struct xpc_channel *ch,
1533 enum xpc_retval reason, unsigned long *irq_flags) 1555 enum xpc_retval reason, unsigned long *irq_flags)
1534{ 1556{
1535 u32 flags; 1557 u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
1536 1558
1537 1559
1538 DBUG_ON(!spin_is_locked(&ch->lock)); 1560 DBUG_ON(!spin_is_locked(&ch->lock));
@@ -1547,61 +1569,53 @@ xpc_disconnect_channel(const int line, struct xpc_channel *ch,
1547 1569
1548 XPC_SET_REASON(ch, reason, line); 1570 XPC_SET_REASON(ch, reason, line);
1549 1571
1550 flags = ch->flags; 1572 ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
1551 /* some of these may not have been set */ 1573 /* some of these may not have been set */
1552 ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | 1574 ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
1553 XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | 1575 XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
1554 XPC_C_CONNECTING | XPC_C_CONNECTED); 1576 XPC_C_CONNECTING | XPC_C_CONNECTED);
1555 1577
1556 ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
1557 xpc_IPI_send_closerequest(ch, irq_flags); 1578 xpc_IPI_send_closerequest(ch, irq_flags);
1558 1579
1559 if (flags & XPC_C_CONNECTED) { 1580 if (channel_was_connected) {
1560 ch->flags |= XPC_C_WASCONNECTED; 1581 ch->flags |= XPC_C_WASCONNECTED;
1561 } 1582 }
1562 1583
1584 spin_unlock_irqrestore(&ch->lock, *irq_flags);
1585
1586 /* wake all idle kthreads so they can exit */
1563 if (atomic_read(&ch->kthreads_idle) > 0) { 1587 if (atomic_read(&ch->kthreads_idle) > 0) {
1564 /* wake all idle kthreads so they can exit */
1565 wake_up_all(&ch->idle_wq); 1588 wake_up_all(&ch->idle_wq);
1566 } 1589 }
1567 1590
1568 spin_unlock_irqrestore(&ch->lock, *irq_flags);
1569
1570
1571 /* wake those waiting to allocate an entry from the local msg queue */ 1591 /* wake those waiting to allocate an entry from the local msg queue */
1572
1573 if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) { 1592 if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) {
1574 wake_up(&ch->msg_allocate_wq); 1593 wake_up(&ch->msg_allocate_wq);
1575 } 1594 }
1576 1595
1577 /* wake those waiting for notify completion */
1578
1579 if (atomic_read(&ch->n_to_notify) > 0) {
1580 xpc_notify_senders(ch, reason, ch->w_local_GP.put);
1581 }
1582
1583 spin_lock_irqsave(&ch->lock, *irq_flags); 1596 spin_lock_irqsave(&ch->lock, *irq_flags);
1584} 1597}
1585 1598
1586 1599
1587void 1600void
1588xpc_disconnected_callout(struct xpc_channel *ch) 1601xpc_disconnecting_callout(struct xpc_channel *ch)
1589{ 1602{
1590 /* 1603 /*
1591 * Let the channel's registerer know that the channel is now 1604 * Let the channel's registerer know that the channel is being
1592 * disconnected. We don't want to do this if the registerer was never 1605 * disconnected. We don't want to do this if the registerer was never
1593 * informed of a connection being made, unless the disconnect was for 1606 * informed of a connection being made.
1594 * abnormal reasons.
1595 */ 1607 */
1596 1608
1597 if (ch->func != NULL) { 1609 if (ch->func != NULL) {
1598 dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " 1610 dev_dbg(xpc_chan, "ch->func() called, reason=xpcDisconnecting,"
1599 "channel=%d\n", ch->reason, ch->partid, ch->number); 1611 " partid=%d, channel=%d\n", ch->partid, ch->number);
1600 1612
1601 ch->func(ch->reason, ch->partid, ch->number, NULL, ch->key); 1613 ch->func(xpcDisconnecting, ch->partid, ch->number, NULL,
1614 ch->key);
1602 1615
1603 dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " 1616 dev_dbg(xpc_chan, "ch->func() returned, reason="
1604 "channel=%d\n", ch->reason, ch->partid, ch->number); 1617 "xpcDisconnecting, partid=%d, channel=%d\n",
1618 ch->partid, ch->number);
1605 } 1619 }
1606} 1620}
1607 1621
@@ -1848,7 +1862,7 @@ xpc_send_msg(struct xpc_channel *ch, struct xpc_msg *msg, u8 notify_type,
1848 xpc_notify_func func, void *key) 1862 xpc_notify_func func, void *key)
1849{ 1863{
1850 enum xpc_retval ret = xpcSuccess; 1864 enum xpc_retval ret = xpcSuccess;
1851 struct xpc_notify *notify = NULL; // >>> to keep the compiler happy!! 1865 struct xpc_notify *notify = notify;
1852 s64 put, msg_number = msg->number; 1866 s64 put, msg_number = msg->number;
1853 1867
1854 1868
diff --git a/arch/ia64/sn/kernel/xpc_main.c b/arch/ia64/sn/kernel/xpc_main.c
index bb1d5cf30440..feece200b3c3 100644
--- a/arch/ia64/sn/kernel/xpc_main.c
+++ b/arch/ia64/sn/kernel/xpc_main.c
@@ -54,6 +54,7 @@
54#include <linux/interrupt.h> 54#include <linux/interrupt.h>
55#include <linux/slab.h> 55#include <linux/slab.h>
56#include <linux/delay.h> 56#include <linux/delay.h>
57#include <linux/reboot.h>
57#include <asm/sn/intr.h> 58#include <asm/sn/intr.h>
58#include <asm/sn/sn_sal.h> 59#include <asm/sn/sn_sal.h>
59#include <asm/uaccess.h> 60#include <asm/uaccess.h>
@@ -82,11 +83,13 @@ struct device *xpc_chan = &xpc_chan_dbg_subname;
82 83
83/* systune related variables for /proc/sys directories */ 84/* systune related variables for /proc/sys directories */
84 85
85static int xpc_hb_min = 1; 86static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;
86static int xpc_hb_max = 10; 87static int xpc_hb_min_interval = 1;
88static int xpc_hb_max_interval = 10;
87 89
88static int xpc_hb_check_min = 10; 90static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL;
89static int xpc_hb_check_max = 120; 91static int xpc_hb_check_min_interval = 10;
92static int xpc_hb_check_max_interval = 120;
90 93
91static ctl_table xpc_sys_xpc_hb_dir[] = { 94static ctl_table xpc_sys_xpc_hb_dir[] = {
92 { 95 {
@@ -99,7 +102,8 @@ static ctl_table xpc_sys_xpc_hb_dir[] = {
99 &proc_dointvec_minmax, 102 &proc_dointvec_minmax,
100 &sysctl_intvec, 103 &sysctl_intvec,
101 NULL, 104 NULL,
102 &xpc_hb_min, &xpc_hb_max 105 &xpc_hb_min_interval,
106 &xpc_hb_max_interval
103 }, 107 },
104 { 108 {
105 2, 109 2,
@@ -111,7 +115,8 @@ static ctl_table xpc_sys_xpc_hb_dir[] = {
111 &proc_dointvec_minmax, 115 &proc_dointvec_minmax,
112 &sysctl_intvec, 116 &sysctl_intvec,
113 NULL, 117 NULL,
114 &xpc_hb_check_min, &xpc_hb_check_max 118 &xpc_hb_check_min_interval,
119 &xpc_hb_check_max_interval
115 }, 120 },
116 {0} 121 {0}
117}; 122};
@@ -148,11 +153,11 @@ static DECLARE_WAIT_QUEUE_HEAD(xpc_act_IRQ_wq);
148 153
149static unsigned long xpc_hb_check_timeout; 154static unsigned long xpc_hb_check_timeout;
150 155
151/* xpc_hb_checker thread exited notification */ 156/* used as an indication of when the xpc_hb_checker thread is inactive */
152static DECLARE_MUTEX_LOCKED(xpc_hb_checker_exited); 157static DECLARE_MUTEX_LOCKED(xpc_hb_checker_inactive);
153 158
154/* xpc_discovery thread exited notification */ 159/* used as an indication of when the xpc_discovery thread is inactive */
155static DECLARE_MUTEX_LOCKED(xpc_discovery_exited); 160static DECLARE_MUTEX_LOCKED(xpc_discovery_inactive);
156 161
157 162
158static struct timer_list xpc_hb_timer; 163static struct timer_list xpc_hb_timer;
@@ -161,6 +166,30 @@ static struct timer_list xpc_hb_timer;
161static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); 166static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *);
162 167
163 168
169static int xpc_system_reboot(struct notifier_block *, unsigned long, void *);
170static struct notifier_block xpc_reboot_notifier = {
171 .notifier_call = xpc_system_reboot,
172};
173
174
175/*
176 * Timer function to enforce the timelimit on the partition disengage request.
177 */
178static void
179xpc_timeout_partition_disengage_request(unsigned long data)
180{
181 struct xpc_partition *part = (struct xpc_partition *) data;
182
183
184 DBUG_ON(XPC_TICKS < part->disengage_request_timeout);
185
186 (void) xpc_partition_disengaged(part);
187
188 DBUG_ON(part->disengage_request_timeout != 0);
189 DBUG_ON(xpc_partition_engaged(1UL << XPC_PARTID(part)) != 0);
190}
191
192
164/* 193/*
165 * Notify the heartbeat check thread that an IRQ has been received. 194 * Notify the heartbeat check thread that an IRQ has been received.
166 */ 195 */
@@ -214,12 +243,6 @@ xpc_hb_checker(void *ignore)
214 243
215 while (!(volatile int) xpc_exiting) { 244 while (!(volatile int) xpc_exiting) {
216 245
217 /* wait for IRQ or timeout */
218 (void) wait_event_interruptible(xpc_act_IRQ_wq,
219 (last_IRQ_count < atomic_read(&xpc_act_IRQ_rcvd) ||
220 jiffies >= xpc_hb_check_timeout ||
221 (volatile int) xpc_exiting));
222
223 dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have " 246 dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have "
224 "been received\n", 247 "been received\n",
225 (int) (xpc_hb_check_timeout - jiffies), 248 (int) (xpc_hb_check_timeout - jiffies),
@@ -240,6 +263,7 @@ xpc_hb_checker(void *ignore)
240 } 263 }
241 264
242 265
266 /* check for outstanding IRQs */
243 new_IRQ_count = atomic_read(&xpc_act_IRQ_rcvd); 267 new_IRQ_count = atomic_read(&xpc_act_IRQ_rcvd);
244 if (last_IRQ_count < new_IRQ_count || force_IRQ != 0) { 268 if (last_IRQ_count < new_IRQ_count || force_IRQ != 0) {
245 force_IRQ = 0; 269 force_IRQ = 0;
@@ -257,13 +281,19 @@ xpc_hb_checker(void *ignore)
257 xpc_hb_check_timeout = jiffies + 281 xpc_hb_check_timeout = jiffies +
258 (xpc_hb_check_interval * HZ); 282 (xpc_hb_check_interval * HZ);
259 } 283 }
284
285 /* wait for IRQ or timeout */
286 (void) wait_event_interruptible(xpc_act_IRQ_wq,
287 (last_IRQ_count < atomic_read(&xpc_act_IRQ_rcvd) ||
288 jiffies >= xpc_hb_check_timeout ||
289 (volatile int) xpc_exiting));
260 } 290 }
261 291
262 dev_dbg(xpc_part, "heartbeat checker is exiting\n"); 292 dev_dbg(xpc_part, "heartbeat checker is exiting\n");
263 293
264 294
265 /* mark this thread as inactive */ 295 /* mark this thread as inactive */
266 up(&xpc_hb_checker_exited); 296 up(&xpc_hb_checker_inactive);
267 return 0; 297 return 0;
268} 298}
269 299
@@ -283,7 +313,7 @@ xpc_initiate_discovery(void *ignore)
283 dev_dbg(xpc_part, "discovery thread is exiting\n"); 313 dev_dbg(xpc_part, "discovery thread is exiting\n");
284 314
285 /* mark this thread as inactive */ 315 /* mark this thread as inactive */
286 up(&xpc_discovery_exited); 316 up(&xpc_discovery_inactive);
287 return 0; 317 return 0;
288} 318}
289 319
@@ -309,7 +339,7 @@ xpc_make_first_contact(struct xpc_partition *part)
309 "partition %d\n", XPC_PARTID(part)); 339 "partition %d\n", XPC_PARTID(part));
310 340
311 /* wait a 1/4 of a second or so */ 341 /* wait a 1/4 of a second or so */
312 msleep_interruptible(250); 342 (void) msleep_interruptible(250);
313 343
314 if (part->act_state == XPC_P_DEACTIVATING) { 344 if (part->act_state == XPC_P_DEACTIVATING) {
315 return part->reason; 345 return part->reason;
@@ -336,7 +366,8 @@ static void
336xpc_channel_mgr(struct xpc_partition *part) 366xpc_channel_mgr(struct xpc_partition *part)
337{ 367{
338 while (part->act_state != XPC_P_DEACTIVATING || 368 while (part->act_state != XPC_P_DEACTIVATING ||
339 atomic_read(&part->nchannels_active) > 0) { 369 atomic_read(&part->nchannels_active) > 0 ||
370 !xpc_partition_disengaged(part)) {
340 371
341 xpc_process_channel_activity(part); 372 xpc_process_channel_activity(part);
342 373
@@ -360,7 +391,8 @@ xpc_channel_mgr(struct xpc_partition *part)
360 (volatile u64) part->local_IPI_amo != 0 || 391 (volatile u64) part->local_IPI_amo != 0 ||
361 ((volatile u8) part->act_state == 392 ((volatile u8) part->act_state ==
362 XPC_P_DEACTIVATING && 393 XPC_P_DEACTIVATING &&
363 atomic_read(&part->nchannels_active) == 0))); 394 atomic_read(&part->nchannels_active) == 0 &&
395 xpc_partition_disengaged(part))));
364 atomic_set(&part->channel_mgr_requests, 1); 396 atomic_set(&part->channel_mgr_requests, 1);
365 397
366 // >>> Does it need to wakeup periodically as well? In case we 398 // >>> Does it need to wakeup periodically as well? In case we
@@ -482,7 +514,7 @@ xpc_activating(void *__partid)
482 return 0; 514 return 0;
483 } 515 }
484 516
485 XPC_ALLOW_HB(partid, xpc_vars); 517 xpc_allow_hb(partid, xpc_vars);
486 xpc_IPI_send_activated(part); 518 xpc_IPI_send_activated(part);
487 519
488 520
@@ -492,6 +524,7 @@ xpc_activating(void *__partid)
492 */ 524 */
493 (void) xpc_partition_up(part); 525 (void) xpc_partition_up(part);
494 526
527 xpc_disallow_hb(partid, xpc_vars);
495 xpc_mark_partition_inactive(part); 528 xpc_mark_partition_inactive(part);
496 529
497 if (part->reason == xpcReactivating) { 530 if (part->reason == xpcReactivating) {
@@ -704,11 +737,14 @@ xpc_daemonize_kthread(void *args)
704 xpc_kthread_waitmsgs(part, ch); 737 xpc_kthread_waitmsgs(part, ch);
705 } 738 }
706 739
707 if (atomic_dec_return(&ch->kthreads_assigned) == 0 && 740 if (atomic_dec_return(&ch->kthreads_assigned) == 0) {
708 ((ch->flags & XPC_C_CONNECTCALLOUT) || 741 if (ch->flags & XPC_C_CONNECTCALLOUT) {
709 (ch->reason != xpcUnregistering && 742 xpc_disconnecting_callout(ch);
710 ch->reason != xpcOtherUnregistering))) { 743 }
711 xpc_disconnected_callout(ch); 744 if (atomic_dec_return(&part->nchannels_engaged) == 0) {
745 xpc_mark_partition_disengaged(part);
746 xpc_IPI_send_disengage(part);
747 }
712 } 748 }
713 749
714 750
@@ -740,6 +776,7 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
740 unsigned long irq_flags; 776 unsigned long irq_flags;
741 pid_t pid; 777 pid_t pid;
742 u64 args = XPC_PACK_ARGS(ch->partid, ch->number); 778 u64 args = XPC_PACK_ARGS(ch->partid, ch->number);
779 struct xpc_partition *part = &xpc_partitions[ch->partid];
743 780
744 781
745 while (needed-- > 0) { 782 while (needed-- > 0) {
@@ -770,9 +807,13 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
770 * kthread. That kthread is responsible for doing the 807 * kthread. That kthread is responsible for doing the
771 * counterpart to the following before it exits. 808 * counterpart to the following before it exits.
772 */ 809 */
773 (void) xpc_part_ref(&xpc_partitions[ch->partid]); 810 (void) xpc_part_ref(part);
774 xpc_msgqueue_ref(ch); 811 xpc_msgqueue_ref(ch);
775 atomic_inc(&ch->kthreads_assigned); 812 if (atomic_inc_return(&ch->kthreads_assigned) == 1) {
813 if (atomic_inc_return(&part->nchannels_engaged) == 1) {
814 xpc_mark_partition_engaged(part);
815 }
816 }
776 ch->kthreads_created++; // >>> temporary debug only!!! 817 ch->kthreads_created++; // >>> temporary debug only!!!
777 } 818 }
778} 819}
@@ -781,6 +822,7 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
781void 822void
782xpc_disconnect_wait(int ch_number) 823xpc_disconnect_wait(int ch_number)
783{ 824{
825 unsigned long irq_flags;
784 partid_t partid; 826 partid_t partid;
785 struct xpc_partition *part; 827 struct xpc_partition *part;
786 struct xpc_channel *ch; 828 struct xpc_channel *ch;
@@ -793,10 +835,13 @@ xpc_disconnect_wait(int ch_number)
793 if (xpc_part_ref(part)) { 835 if (xpc_part_ref(part)) {
794 ch = &part->channels[ch_number]; 836 ch = &part->channels[ch_number];
795 837
796// >>> how do we keep from falling into the window between our check and going 838 if (ch->flags & XPC_C_WDISCONNECT) {
797// >>> down and coming back up where sema is re-inited? 839 if (!(ch->flags & XPC_C_DISCONNECTED)) {
798 if (ch->flags & XPC_C_SETUP) { 840 (void) down(&ch->wdisconnect_sema);
799 (void) down(&ch->teardown_sema); 841 }
842 spin_lock_irqsave(&ch->lock, irq_flags);
843 ch->flags &= ~XPC_C_WDISCONNECT;
844 spin_unlock_irqrestore(&ch->lock, irq_flags);
800 } 845 }
801 846
802 xpc_part_deref(part); 847 xpc_part_deref(part);
@@ -806,62 +851,89 @@ xpc_disconnect_wait(int ch_number)
806 851
807 852
808static void 853static void
809xpc_do_exit(void) 854xpc_do_exit(enum xpc_retval reason)
810{ 855{
811 partid_t partid; 856 partid_t partid;
812 int active_part_count; 857 int active_part_count;
813 struct xpc_partition *part; 858 struct xpc_partition *part;
859 unsigned long printmsg_time;
814 860
815 861
816 /* now it's time to eliminate our heartbeat */ 862 /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */
817 del_timer_sync(&xpc_hb_timer); 863 DBUG_ON(xpc_exiting == 1);
818 xpc_vars->heartbeating_to_mask = 0;
819
820 /* indicate to others that our reserved page is uninitialized */
821 xpc_rsvd_page->vars_pa = 0;
822 864
823 /* 865 /*
824 * Ignore all incoming interrupts. Without interupts the heartbeat 866 * Let the heartbeat checker thread and the discovery thread
825 * checker won't activate any new partitions that may come up. 867 * (if one is running) know that they should exit. Also wake up
826 */ 868 * the heartbeat checker thread in case it's sleeping.
827 free_irq(SGI_XPC_ACTIVATE, NULL);
828
829 /*
830 * Cause the heartbeat checker and the discovery threads to exit.
831 * We don't want them attempting to activate new partitions as we
832 * try to deactivate the existing ones.
833 */ 869 */
834 xpc_exiting = 1; 870 xpc_exiting = 1;
835 wake_up_interruptible(&xpc_act_IRQ_wq); 871 wake_up_interruptible(&xpc_act_IRQ_wq);
836 872
837 /* wait for the heartbeat checker thread to mark itself inactive */ 873 /* ignore all incoming interrupts */
838 down(&xpc_hb_checker_exited); 874 free_irq(SGI_XPC_ACTIVATE, NULL);
839 875
840 /* wait for the discovery thread to mark itself inactive */ 876 /* wait for the discovery thread to mark itself inactive */
841 down(&xpc_discovery_exited); 877 down(&xpc_discovery_inactive);
878
879 /* wait for the heartbeat checker thread to mark itself inactive */
880 down(&xpc_hb_checker_inactive);
842 881
843 882
844 msleep_interruptible(300); 883 /* sleep for a 1/3 of a second or so */
884 (void) msleep_interruptible(300);
845 885
846 886
847 /* wait for all partitions to become inactive */ 887 /* wait for all partitions to become inactive */
848 888
889 printmsg_time = jiffies;
890
849 do { 891 do {
850 active_part_count = 0; 892 active_part_count = 0;
851 893
852 for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { 894 for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
853 part = &xpc_partitions[partid]; 895 part = &xpc_partitions[partid];
854 if (part->act_state != XPC_P_INACTIVE) { 896 if (xpc_partition_disengaged(part) &&
855 active_part_count++; 897 part->act_state == XPC_P_INACTIVE) {
856 898 continue;
857 XPC_DEACTIVATE_PARTITION(part, xpcUnloading);
858 } 899 }
900
901 active_part_count++;
902
903 XPC_DEACTIVATE_PARTITION(part, reason);
904 }
905
906 if (active_part_count == 0) {
907 break;
908 }
909
910 if (jiffies >= printmsg_time) {
911 dev_info(xpc_part, "waiting for partitions to "
912 "deactivate/disengage, active count=%d, remote "
913 "engaged=0x%lx\n", active_part_count,
914 xpc_partition_engaged(1UL << partid));
915
916 printmsg_time = jiffies +
917 (XPC_DISENGAGE_PRINTMSG_INTERVAL * HZ);
859 } 918 }
860 919
861 if (active_part_count) 920 /* sleep for a 1/3 of a second or so */
862 msleep_interruptible(300); 921 (void) msleep_interruptible(300);
863 } while (active_part_count > 0); 922
923 } while (1);
924
925 DBUG_ON(xpc_partition_engaged(-1UL));
926
927
928 /* indicate to others that our reserved page is uninitialized */
929 xpc_rsvd_page->vars_pa = 0;
930
931 /* now it's time to eliminate our heartbeat */
932 del_timer_sync(&xpc_hb_timer);
933 DBUG_ON(xpc_vars->heartbeating_to_mask == 0);
864 934
935 /* take ourselves off of the reboot_notifier_list */
936 (void) unregister_reboot_notifier(&xpc_reboot_notifier);
865 937
866 /* close down protections for IPI operations */ 938 /* close down protections for IPI operations */
867 xpc_restrict_IPI_ops(); 939 xpc_restrict_IPI_ops();
@@ -876,6 +948,34 @@ xpc_do_exit(void)
876} 948}
877 949
878 950
951/*
952 * This function is called when the system is being rebooted.
953 */
954static int
955xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused)
956{
957 enum xpc_retval reason;
958
959
960 switch (event) {
961 case SYS_RESTART:
962 reason = xpcSystemReboot;
963 break;
964 case SYS_HALT:
965 reason = xpcSystemHalt;
966 break;
967 case SYS_POWER_OFF:
968 reason = xpcSystemPoweroff;
969 break;
970 default:
971 reason = xpcSystemGoingDown;
972 }
973
974 xpc_do_exit(reason);
975 return NOTIFY_DONE;
976}
977
978
879int __init 979int __init
880xpc_init(void) 980xpc_init(void)
881{ 981{
@@ -920,6 +1020,12 @@ xpc_init(void)
920 spin_lock_init(&part->act_lock); 1020 spin_lock_init(&part->act_lock);
921 part->act_state = XPC_P_INACTIVE; 1021 part->act_state = XPC_P_INACTIVE;
922 XPC_SET_REASON(part, 0, 0); 1022 XPC_SET_REASON(part, 0, 0);
1023
1024 init_timer(&part->disengage_request_timer);
1025 part->disengage_request_timer.function =
1026 xpc_timeout_partition_disengage_request;
1027 part->disengage_request_timer.data = (unsigned long) part;
1028
923 part->setup_state = XPC_P_UNSET; 1029 part->setup_state = XPC_P_UNSET;
924 init_waitqueue_head(&part->teardown_wq); 1030 init_waitqueue_head(&part->teardown_wq);
925 atomic_set(&part->references, 0); 1031 atomic_set(&part->references, 0);
@@ -976,6 +1082,13 @@ xpc_init(void)
976 } 1082 }
977 1083
978 1084
1085 /* add ourselves to the reboot_notifier_list */
1086 ret = register_reboot_notifier(&xpc_reboot_notifier);
1087 if (ret != 0) {
1088 dev_warn(xpc_part, "can't register reboot notifier\n");
1089 }
1090
1091
979 /* 1092 /*
980 * Set the beating to other partitions into motion. This is 1093 * Set the beating to other partitions into motion. This is
981 * the last requirement for other partitions' discovery to 1094 * the last requirement for other partitions' discovery to
@@ -997,6 +1110,9 @@ xpc_init(void)
997 /* indicate to others that our reserved page is uninitialized */ 1110 /* indicate to others that our reserved page is uninitialized */
998 xpc_rsvd_page->vars_pa = 0; 1111 xpc_rsvd_page->vars_pa = 0;
999 1112
1113 /* take ourselves off of the reboot_notifier_list */
1114 (void) unregister_reboot_notifier(&xpc_reboot_notifier);
1115
1000 del_timer_sync(&xpc_hb_timer); 1116 del_timer_sync(&xpc_hb_timer);
1001 free_irq(SGI_XPC_ACTIVATE, NULL); 1117 free_irq(SGI_XPC_ACTIVATE, NULL);
1002 xpc_restrict_IPI_ops(); 1118 xpc_restrict_IPI_ops();
@@ -1018,9 +1134,9 @@ xpc_init(void)
1018 dev_err(xpc_part, "failed while forking discovery thread\n"); 1134 dev_err(xpc_part, "failed while forking discovery thread\n");
1019 1135
1020 /* mark this new thread as a non-starter */ 1136 /* mark this new thread as a non-starter */
1021 up(&xpc_discovery_exited); 1137 up(&xpc_discovery_inactive);
1022 1138
1023 xpc_do_exit(); 1139 xpc_do_exit(xpcUnloading);
1024 return -EBUSY; 1140 return -EBUSY;
1025 } 1141 }
1026 1142
@@ -1039,7 +1155,7 @@ module_init(xpc_init);
1039void __exit 1155void __exit
1040xpc_exit(void) 1156xpc_exit(void)
1041{ 1157{
1042 xpc_do_exit(); 1158 xpc_do_exit(xpcUnloading);
1043} 1159}
1044module_exit(xpc_exit); 1160module_exit(xpc_exit);
1045 1161
diff --git a/arch/ia64/sn/kernel/xpc_partition.c b/arch/ia64/sn/kernel/xpc_partition.c
index 578265ea9e67..79a0fc4c860c 100644
--- a/arch/ia64/sn/kernel/xpc_partition.c
+++ b/arch/ia64/sn/kernel/xpc_partition.c
@@ -76,11 +76,6 @@ char ____cacheline_aligned
76 xpc_remote_copy_buffer[XPC_RSVD_PAGE_ALIGNED_SIZE]; 76 xpc_remote_copy_buffer[XPC_RSVD_PAGE_ALIGNED_SIZE];
77 77
78 78
79/* systune related variables */
80int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;
81int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_TIMEOUT;
82
83
84/* 79/*
85 * Given a nasid, get the physical address of the partition's reserved page 80 * Given a nasid, get the physical address of the partition's reserved page
86 * for that nasid. This function returns 0 on any error. 81 * for that nasid. This function returns 0 on any error.
@@ -239,16 +234,21 @@ xpc_rsvd_page_init(void)
239 xpc_vars->amos_page = amos_page; /* save for next load of XPC */ 234 xpc_vars->amos_page = amos_page; /* save for next load of XPC */
240 235
241 236
242 /* 237 /* initialize the activate IRQ related AMO variables */
243 * Initialize the activation related AMO variables. 238 for (i = 0; i < XP_NASID_MASK_WORDS; i++) {
244 */ 239 (void) xpc_IPI_init(XPC_ACTIVATE_IRQ_AMOS + i);
245 xpc_vars->act_amos = xpc_IPI_init(XP_MAX_PARTITIONS);
246 for (i = 1; i < XP_NASID_MASK_WORDS; i++) {
247 xpc_IPI_init(i + XP_MAX_PARTITIONS);
248 } 240 }
241
242 /* initialize the engaged remote partitions related AMO variables */
243 (void) xpc_IPI_init(XPC_ENGAGED_PARTITIONS_AMO);
244 (void) xpc_IPI_init(XPC_DISENGAGE_REQUEST_AMO);
245
249 /* export AMO page's physical address to other partitions */ 246 /* export AMO page's physical address to other partitions */
250 xpc_vars->amos_page_pa = ia64_tpa((u64) xpc_vars->amos_page); 247 xpc_vars->amos_page_pa = ia64_tpa((u64) xpc_vars->amos_page);
251 248
249 /* timestamp of when reserved page was initialized */
250 rp->stamp = CURRENT_TIME;
251
252 /* 252 /*
253 * This signifies to the remote partition that our reserved 253 * This signifies to the remote partition that our reserved
254 * page is initialized. 254 * page is initialized.
@@ -387,6 +387,11 @@ xpc_check_remote_hb(void)
387 remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer; 387 remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer;
388 388
389 for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { 389 for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
390
391 if (xpc_exiting) {
392 break;
393 }
394
390 if (partid == sn_partition_id) { 395 if (partid == sn_partition_id) {
391 continue; 396 continue;
392 } 397 }
@@ -417,7 +422,7 @@ xpc_check_remote_hb(void)
417 422
418 if (((remote_vars->heartbeat == part->last_heartbeat) && 423 if (((remote_vars->heartbeat == part->last_heartbeat) &&
419 (remote_vars->kdb_status == 0)) || 424 (remote_vars->kdb_status == 0)) ||
420 !XPC_HB_ALLOWED(sn_partition_id, remote_vars)) { 425 !xpc_hb_allowed(sn_partition_id, remote_vars)) {
421 426
422 XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat); 427 XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat);
423 continue; 428 continue;
@@ -436,23 +441,23 @@ xpc_check_remote_hb(void)
436 */ 441 */
437static enum xpc_retval 442static enum xpc_retval
438xpc_get_remote_rp(int nasid, u64 *discovered_nasids, 443xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
439 struct xpc_rsvd_page *remote_rp, u64 *remote_rsvd_page_pa) 444 struct xpc_rsvd_page *remote_rp, u64 *remote_rp_pa)
440{ 445{
441 int bres, i; 446 int bres, i;
442 447
443 448
444 /* get the reserved page's physical address */ 449 /* get the reserved page's physical address */
445 450
446 *remote_rsvd_page_pa = xpc_get_rsvd_page_pa(nasid, (u64) remote_rp, 451 *remote_rp_pa = xpc_get_rsvd_page_pa(nasid, (u64) remote_rp,
447 XPC_RSVD_PAGE_ALIGNED_SIZE); 452 XPC_RSVD_PAGE_ALIGNED_SIZE);
448 if (*remote_rsvd_page_pa == 0) { 453 if (*remote_rp_pa == 0) {
449 return xpcNoRsvdPageAddr; 454 return xpcNoRsvdPageAddr;
450 } 455 }
451 456
452 457
453 /* pull over the reserved page structure */ 458 /* pull over the reserved page structure */
454 459
455 bres = xp_bte_copy(*remote_rsvd_page_pa, ia64_tpa((u64) remote_rp), 460 bres = xp_bte_copy(*remote_rp_pa, ia64_tpa((u64) remote_rp),
456 XPC_RSVD_PAGE_ALIGNED_SIZE, 461 XPC_RSVD_PAGE_ALIGNED_SIZE,
457 (BTE_NOTIFY | BTE_WACQUIRE), NULL); 462 (BTE_NOTIFY | BTE_WACQUIRE), NULL);
458 if (bres != BTE_SUCCESS) { 463 if (bres != BTE_SUCCESS) {
@@ -524,6 +529,55 @@ xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars)
524 529
525 530
526/* 531/*
532 * Update the remote partition's info.
533 */
534static void
535xpc_update_partition_info(struct xpc_partition *part, u8 remote_rp_version,
536 struct timespec *remote_rp_stamp, u64 remote_rp_pa,
537 u64 remote_vars_pa, struct xpc_vars *remote_vars)
538{
539 part->remote_rp_version = remote_rp_version;
540 dev_dbg(xpc_part, " remote_rp_version = 0x%016lx\n",
541 part->remote_rp_version);
542
543 part->remote_rp_stamp = *remote_rp_stamp;
544 dev_dbg(xpc_part, " remote_rp_stamp (tv_sec = 0x%lx tv_nsec = 0x%lx\n",
545 part->remote_rp_stamp.tv_sec, part->remote_rp_stamp.tv_nsec);
546
547 part->remote_rp_pa = remote_rp_pa;
548 dev_dbg(xpc_part, " remote_rp_pa = 0x%016lx\n", part->remote_rp_pa);
549
550 part->remote_vars_pa = remote_vars_pa;
551 dev_dbg(xpc_part, " remote_vars_pa = 0x%016lx\n",
552 part->remote_vars_pa);
553
554 part->last_heartbeat = remote_vars->heartbeat;
555 dev_dbg(xpc_part, " last_heartbeat = 0x%016lx\n",
556 part->last_heartbeat);
557
558 part->remote_vars_part_pa = remote_vars->vars_part_pa;
559 dev_dbg(xpc_part, " remote_vars_part_pa = 0x%016lx\n",
560 part->remote_vars_part_pa);
561
562 part->remote_act_nasid = remote_vars->act_nasid;
563 dev_dbg(xpc_part, " remote_act_nasid = 0x%x\n",
564 part->remote_act_nasid);
565
566 part->remote_act_phys_cpuid = remote_vars->act_phys_cpuid;
567 dev_dbg(xpc_part, " remote_act_phys_cpuid = 0x%x\n",
568 part->remote_act_phys_cpuid);
569
570 part->remote_amos_page_pa = remote_vars->amos_page_pa;
571 dev_dbg(xpc_part, " remote_amos_page_pa = 0x%lx\n",
572 part->remote_amos_page_pa);
573
574 part->remote_vars_version = remote_vars->version;
575 dev_dbg(xpc_part, " remote_vars_version = 0x%x\n",
576 part->remote_vars_version);
577}
578
579
580/*
527 * Prior code has determine the nasid which generated an IPI. Inspect 581 * Prior code has determine the nasid which generated an IPI. Inspect
528 * that nasid to determine if its partition needs to be activated or 582 * that nasid to determine if its partition needs to be activated or
529 * deactivated. 583 * deactivated.
@@ -542,8 +596,12 @@ xpc_identify_act_IRQ_req(int nasid)
542{ 596{
543 struct xpc_rsvd_page *remote_rp; 597 struct xpc_rsvd_page *remote_rp;
544 struct xpc_vars *remote_vars; 598 struct xpc_vars *remote_vars;
545 u64 remote_rsvd_page_pa; 599 u64 remote_rp_pa;
546 u64 remote_vars_pa; 600 u64 remote_vars_pa;
601 int remote_rp_version;
602 int reactivate = 0;
603 int stamp_diff;
604 struct timespec remote_rp_stamp = { 0, 0 };
547 partid_t partid; 605 partid_t partid;
548 struct xpc_partition *part; 606 struct xpc_partition *part;
549 enum xpc_retval ret; 607 enum xpc_retval ret;
@@ -553,7 +611,7 @@ xpc_identify_act_IRQ_req(int nasid)
553 611
554 remote_rp = (struct xpc_rsvd_page *) xpc_remote_copy_buffer; 612 remote_rp = (struct xpc_rsvd_page *) xpc_remote_copy_buffer;
555 613
556 ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rsvd_page_pa); 614 ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rp_pa);
557 if (ret != xpcSuccess) { 615 if (ret != xpcSuccess) {
558 dev_warn(xpc_part, "unable to get reserved page from nasid %d, " 616 dev_warn(xpc_part, "unable to get reserved page from nasid %d, "
559 "which sent interrupt, reason=%d\n", nasid, ret); 617 "which sent interrupt, reason=%d\n", nasid, ret);
@@ -561,6 +619,10 @@ xpc_identify_act_IRQ_req(int nasid)
561 } 619 }
562 620
563 remote_vars_pa = remote_rp->vars_pa; 621 remote_vars_pa = remote_rp->vars_pa;
622 remote_rp_version = remote_rp->version;
623 if (XPC_SUPPORTS_RP_STAMP(remote_rp_version)) {
624 remote_rp_stamp = remote_rp->stamp;
625 }
564 partid = remote_rp->partid; 626 partid = remote_rp->partid;
565 part = &xpc_partitions[partid]; 627 part = &xpc_partitions[partid];
566 628
@@ -586,44 +648,117 @@ xpc_identify_act_IRQ_req(int nasid)
586 "%ld:0x%lx\n", (int) nasid, (int) partid, part->act_IRQ_rcvd, 648 "%ld:0x%lx\n", (int) nasid, (int) partid, part->act_IRQ_rcvd,
587 remote_vars->heartbeat, remote_vars->heartbeating_to_mask); 649 remote_vars->heartbeat, remote_vars->heartbeating_to_mask);
588 650
651 if (xpc_partition_disengaged(part) &&
652 part->act_state == XPC_P_INACTIVE) {
589 653
590 if (part->act_state == XPC_P_INACTIVE) { 654 xpc_update_partition_info(part, remote_rp_version,
655 &remote_rp_stamp, remote_rp_pa,
656 remote_vars_pa, remote_vars);
591 657
592 part->remote_rp_pa = remote_rsvd_page_pa; 658 if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) {
593 dev_dbg(xpc_part, " remote_rp_pa = 0x%016lx\n", 659 if (xpc_partition_disengage_requested(1UL << partid)) {
594 part->remote_rp_pa); 660 /*
661 * Other side is waiting on us to disengage,
662 * even though we already have.
663 */
664 return;
665 }
666 } else {
667 /* other side doesn't support disengage requests */
668 xpc_clear_partition_disengage_request(1UL << partid);
669 }
595 670
596 part->remote_vars_pa = remote_vars_pa; 671 xpc_activate_partition(part);
597 dev_dbg(xpc_part, " remote_vars_pa = 0x%016lx\n", 672 return;
598 part->remote_vars_pa); 673 }
599 674
600 part->last_heartbeat = remote_vars->heartbeat; 675 DBUG_ON(part->remote_rp_version == 0);
601 dev_dbg(xpc_part, " last_heartbeat = 0x%016lx\n", 676 DBUG_ON(part->remote_vars_version == 0);
602 part->last_heartbeat); 677
678 if (!XPC_SUPPORTS_RP_STAMP(part->remote_rp_version)) {
679 DBUG_ON(XPC_SUPPORTS_DISENGAGE_REQUEST(part->
680 remote_vars_version));
681
682 if (!XPC_SUPPORTS_RP_STAMP(remote_rp_version)) {
683 DBUG_ON(XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->
684 version));
685 /* see if the other side rebooted */
686 if (part->remote_amos_page_pa ==
687 remote_vars->amos_page_pa &&
688 xpc_hb_allowed(sn_partition_id,
689 remote_vars)) {
690 /* doesn't look that way, so ignore the IPI */
691 return;
692 }
693 }
603 694
604 part->remote_vars_part_pa = remote_vars->vars_part_pa; 695 /*
605 dev_dbg(xpc_part, " remote_vars_part_pa = 0x%016lx\n", 696 * Other side rebooted and previous XPC didn't support the
606 part->remote_vars_part_pa); 697 * disengage request, so we don't need to do anything special.
698 */
607 699
608 part->remote_act_nasid = remote_vars->act_nasid; 700 xpc_update_partition_info(part, remote_rp_version,
609 dev_dbg(xpc_part, " remote_act_nasid = 0x%x\n", 701 &remote_rp_stamp, remote_rp_pa,
610 part->remote_act_nasid); 702 remote_vars_pa, remote_vars);
703 part->reactivate_nasid = nasid;
704 XPC_DEACTIVATE_PARTITION(part, xpcReactivating);
705 return;
706 }
611 707
612 part->remote_act_phys_cpuid = remote_vars->act_phys_cpuid; 708 DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version));
613 dev_dbg(xpc_part, " remote_act_phys_cpuid = 0x%x\n",
614 part->remote_act_phys_cpuid);
615 709
616 part->remote_amos_page_pa = remote_vars->amos_page_pa; 710 if (!XPC_SUPPORTS_RP_STAMP(remote_rp_version)) {
617 dev_dbg(xpc_part, " remote_amos_page_pa = 0x%lx\n", 711 DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->version));
618 part->remote_amos_page_pa);
619 712
620 xpc_activate_partition(part); 713 /*
714 * Other side rebooted and previous XPC did support the
715 * disengage request, but the new one doesn't.
716 */
621 717
622 } else if (part->remote_amos_page_pa != remote_vars->amos_page_pa || 718 xpc_clear_partition_engaged(1UL << partid);
623 !XPC_HB_ALLOWED(sn_partition_id, remote_vars)) { 719 xpc_clear_partition_disengage_request(1UL << partid);
624 720
721 xpc_update_partition_info(part, remote_rp_version,
722 &remote_rp_stamp, remote_rp_pa,
723 remote_vars_pa, remote_vars);
724 reactivate = 1;
725
726 } else {
727 DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->version));
728
729 stamp_diff = xpc_compare_stamps(&part->remote_rp_stamp,
730 &remote_rp_stamp);
731 if (stamp_diff != 0) {
732 DBUG_ON(stamp_diff >= 0);
733
734 /*
735 * Other side rebooted and the previous XPC did support
736 * the disengage request, as does the new one.
737 */
738
739 DBUG_ON(xpc_partition_engaged(1UL << partid));
740 DBUG_ON(xpc_partition_disengage_requested(1UL <<
741 partid));
742
743 xpc_update_partition_info(part, remote_rp_version,
744 &remote_rp_stamp, remote_rp_pa,
745 remote_vars_pa, remote_vars);
746 reactivate = 1;
747 }
748 }
749
750 if (!xpc_partition_disengaged(part)) {
751 /* still waiting on other side to disengage from us */
752 return;
753 }
754
755 if (reactivate) {
625 part->reactivate_nasid = nasid; 756 part->reactivate_nasid = nasid;
626 XPC_DEACTIVATE_PARTITION(part, xpcReactivating); 757 XPC_DEACTIVATE_PARTITION(part, xpcReactivating);
758
759 } else if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version) &&
760 xpc_partition_disengage_requested(1UL << partid)) {
761 XPC_DEACTIVATE_PARTITION(part, xpcOtherGoingDown);
627 } 762 }
628} 763}
629 764
@@ -646,12 +781,16 @@ xpc_identify_act_IRQ_sender(void)
646 struct xpc_rsvd_page *rp = (struct xpc_rsvd_page *) xpc_rsvd_page; 781 struct xpc_rsvd_page *rp = (struct xpc_rsvd_page *) xpc_rsvd_page;
647 782
648 783
649 act_amos = xpc_vars->act_amos; 784 act_amos = xpc_vars->amos_page + XPC_ACTIVATE_IRQ_AMOS;
650 785
651 786
652 /* scan through act AMO variable looking for non-zero entries */ 787 /* scan through act AMO variable looking for non-zero entries */
653 for (word = 0; word < XP_NASID_MASK_WORDS; word++) { 788 for (word = 0; word < XP_NASID_MASK_WORDS; word++) {
654 789
790 if (xpc_exiting) {
791 break;
792 }
793
655 nasid_mask = xpc_IPI_receive(&act_amos[word]); 794 nasid_mask = xpc_IPI_receive(&act_amos[word]);
656 if (nasid_mask == 0) { 795 if (nasid_mask == 0) {
657 /* no IRQs from nasids in this variable */ 796 /* no IRQs from nasids in this variable */
@@ -688,6 +827,55 @@ xpc_identify_act_IRQ_sender(void)
688 827
689 828
690/* 829/*
830 * See if the other side has responded to a partition disengage request
831 * from us.
832 */
833int
834xpc_partition_disengaged(struct xpc_partition *part)
835{
836 partid_t partid = XPC_PARTID(part);
837 int disengaged;
838
839
840 disengaged = (xpc_partition_engaged(1UL << partid) == 0);
841 if (part->disengage_request_timeout) {
842 if (!disengaged) {
843 if (jiffies < part->disengage_request_timeout) {
844 /* timelimit hasn't been reached yet */
845 return 0;
846 }
847
848 /*
849 * Other side hasn't responded to our disengage
850 * request in a timely fashion, so assume it's dead.
851 */
852
853 xpc_clear_partition_engaged(1UL << partid);
854 disengaged = 1;
855 }
856 part->disengage_request_timeout = 0;
857
858 /* cancel the timer function, provided it's not us */
859 if (!in_interrupt()) {
860 del_singleshot_timer_sync(&part->
861 disengage_request_timer);
862 }
863
864 DBUG_ON(part->act_state != XPC_P_DEACTIVATING &&
865 part->act_state != XPC_P_INACTIVE);
866 if (part->act_state != XPC_P_INACTIVE) {
867 xpc_wakeup_channel_mgr(part);
868 }
869
870 if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) {
871 xpc_cancel_partition_disengage_request(part);
872 }
873 }
874 return disengaged;
875}
876
877
878/*
691 * Mark specified partition as active. 879 * Mark specified partition as active.
692 */ 880 */
693enum xpc_retval 881enum xpc_retval
@@ -721,7 +909,6 @@ xpc_deactivate_partition(const int line, struct xpc_partition *part,
721 enum xpc_retval reason) 909 enum xpc_retval reason)
722{ 910{
723 unsigned long irq_flags; 911 unsigned long irq_flags;
724 partid_t partid = XPC_PARTID(part);
725 912
726 913
727 spin_lock_irqsave(&part->act_lock, irq_flags); 914 spin_lock_irqsave(&part->act_lock, irq_flags);
@@ -749,17 +936,27 @@ xpc_deactivate_partition(const int line, struct xpc_partition *part,
749 936
750 spin_unlock_irqrestore(&part->act_lock, irq_flags); 937 spin_unlock_irqrestore(&part->act_lock, irq_flags);
751 938
752 XPC_DISALLOW_HB(partid, xpc_vars); 939 if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) {
940 xpc_request_partition_disengage(part);
941 xpc_IPI_send_disengage(part);
942
943 /* set a timelimit on the disengage request */
944 part->disengage_request_timeout = jiffies +
945 (XPC_DISENGAGE_REQUEST_TIMELIMIT * HZ);
946 part->disengage_request_timer.expires =
947 part->disengage_request_timeout;
948 add_timer(&part->disengage_request_timer);
949 }
753 950
754 dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", partid, 951 dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", partid,
755 reason); 952 reason);
756 953
757 xpc_partition_down(part, reason); 954 xpc_partition_going_down(part, reason);
758} 955}
759 956
760 957
761/* 958/*
762 * Mark specified partition as active. 959 * Mark specified partition as inactive.
763 */ 960 */
764void 961void
765xpc_mark_partition_inactive(struct xpc_partition *part) 962xpc_mark_partition_inactive(struct xpc_partition *part)
@@ -792,7 +989,7 @@ xpc_discovery(void)
792 void *remote_rp_base; 989 void *remote_rp_base;
793 struct xpc_rsvd_page *remote_rp; 990 struct xpc_rsvd_page *remote_rp;
794 struct xpc_vars *remote_vars; 991 struct xpc_vars *remote_vars;
795 u64 remote_rsvd_page_pa; 992 u64 remote_rp_pa;
796 u64 remote_vars_pa; 993 u64 remote_vars_pa;
797 int region; 994 int region;
798 int max_regions; 995 int max_regions;
@@ -877,7 +1074,7 @@ xpc_discovery(void)
877 /* pull over the reserved page structure */ 1074 /* pull over the reserved page structure */
878 1075
879 ret = xpc_get_remote_rp(nasid, discovered_nasids, 1076 ret = xpc_get_remote_rp(nasid, discovered_nasids,
880 remote_rp, &remote_rsvd_page_pa); 1077 remote_rp, &remote_rp_pa);
881 if (ret != xpcSuccess) { 1078 if (ret != xpcSuccess) {
882 dev_dbg(xpc_part, "unable to get reserved page " 1079 dev_dbg(xpc_part, "unable to get reserved page "
883 "from nasid %d, reason=%d\n", nasid, 1080 "from nasid %d, reason=%d\n", nasid,
@@ -948,6 +1145,13 @@ xpc_discovery(void)
948 remote_vars->act_nasid, 1145 remote_vars->act_nasid,
949 remote_vars->act_phys_cpuid); 1146 remote_vars->act_phys_cpuid);
950 1147
1148 if (XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->
1149 version)) {
1150 part->remote_amos_page_pa =
1151 remote_vars->amos_page_pa;
1152 xpc_mark_partition_disengaged(part);
1153 xpc_cancel_partition_disengage_request(part);
1154 }
951 xpc_IPI_send_activate(remote_vars); 1155 xpc_IPI_send_activate(remote_vars);
952 } 1156 }
953 } 1157 }
diff --git a/include/asm-ia64/sn/xp.h b/include/asm-ia64/sn/xp.h
index 1df1c9f61a65..f3052a54932b 100644
--- a/include/asm-ia64/sn/xp.h
+++ b/include/asm-ia64/sn/xp.h
@@ -217,7 +217,15 @@ enum xpc_retval {
217 xpcInvalidPartid, /* 42: invalid partition ID */ 217 xpcInvalidPartid, /* 42: invalid partition ID */
218 xpcLocalPartid, /* 43: local partition ID */ 218 xpcLocalPartid, /* 43: local partition ID */
219 219
220 xpcUnknownReason /* 44: unknown reason -- must be last in list */ 220 xpcOtherGoingDown, /* 44: other side going down, reason unknown */
221 xpcSystemGoingDown, /* 45: system is going down, reason unknown */
222 xpcSystemHalt, /* 46: system is being halted */
223 xpcSystemReboot, /* 47: system is being rebooted */
224 xpcSystemPoweroff, /* 48: system is being powered off */
225
226 xpcDisconnecting, /* 49: channel disconnecting (closing) */
227
228 xpcUnknownReason /* 50: unknown reason -- must be last in list */
221}; 229};
222 230
223 231