patch-2.4.13 linux/arch/s390x/kernel/smp.c

Next file: linux/arch/s390x/kernel/time.c
Previous file: linux/arch/s390x/kernel/signal32.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.12/linux/arch/s390x/kernel/smp.c linux/arch/s390x/kernel/smp.c
@@ -57,6 +57,8 @@
 
 spinlock_t       kernel_flag = SPIN_LOCK_UNLOCKED;
 
+unsigned long	 cpu_online_map;
+
 /*
  *      Setup routine for controlling SMP activation
  *
@@ -92,6 +94,95 @@
 
 extern void reipl(unsigned long devno);
 
+static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
+static void smp_ext_bitcall_others(ec_bit_sig);
+
+/*
+ * Structure and data for smp_call_function(). This is designed to minimise
+ * static memory requirements. It also looks cleaner.
+ */
+static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
+
+struct call_data_struct {
+	void (*func) (void *info);
+	void *info;
+	atomic_t started;
+	atomic_t finished;
+	int wait;
+};
+
+static struct call_data_struct * call_data;
+
+/*
+ * 'Call function' interrupt callback
+ */
+static void do_call_function(void)
+{
+	void (*func) (void *info) = call_data->func;
+	void *info = call_data->info;
+	int wait = call_data->wait;
+
+	atomic_inc(&call_data->started);
+	(*func)(info);
+	if (wait)
+		atomic_inc(&call_data->finished);
+}
+
+/*
+ * this function sends a 'generic call function' IPI to all other CPUs
+ * in the system.
+ */
+
+int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
+			int wait)
+/*
+ * [SUMMARY] Run a function on all other CPUs.
+ * <func> The function to run. This must be fast and non-blocking.
+ * <info> An arbitrary pointer to pass to the function.
+ * <nonatomic> currently unused.
+ * <wait> If true, wait (atomically) until function has completed on other CPUs.
+ * [RETURNS] 0 on success, else a negative status code. Does not return until
+ * remote CPUs are nearly ready to execute <<func>> or are or have executed.
+ *
+ * You must not call this function with disabled interrupts or from a
+ * hardware interrupt handler, you may call it from a bottom half handler.
+ */
+{
+	struct call_data_struct data;
+	int cpus = smp_num_cpus-1;
+
+	if (!cpus || !atomic_read(&smp_commenced))
+		return 0;
+
+	data.func = func;
+	data.info = info;
+	atomic_set(&data.started, 0);
+	data.wait = wait;
+	if (wait)
+		atomic_set(&data.finished, 0);
+
+	spin_lock_bh(&call_lock);
+	call_data = &data;
+	/* Send a message to all other CPUs and wait for them to respond */
+        smp_ext_bitcall_others(ec_call_function);
+
+	/* Wait for response */
+	while (atomic_read(&data.started) != cpus)
+		barrier();
+
+	if (wait)
+		while (atomic_read(&data.finished) != cpus)
+			barrier();
+	spin_unlock_bh(&call_lock);
+
+	return 0;
+}
+
+
+/*
+ * Various special callbacks
+ */
+
 void do_machine_restart(void)
 {
         smp_send_stop();
@@ -148,7 +239,6 @@
 
 void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
 {
-        ec_ext_call *ec, *next;
         unsigned long bits;
 
         /*
@@ -167,138 +257,15 @@
 		do_machine_halt();
         if (test_bit(ec_power_off, &bits))
 		do_machine_power_off();
-
-        /*
-         * Handle external call commands with a parameter area
-         */
-	ec = (ec_ext_call *) xchg(&S390_lowcore.ext_call_queue, 0);
-        if (ec == NULL)
-                return;   /* no command signals */
-
-        /* Make a fifo out of the lifo */
-        next = ec->next;
-        ec->next = NULL;
-        while (next != NULL) {
-                ec_ext_call *tmp = next->next;
-                next->next = ec;
-                ec = next;
-                next = tmp;
-        }
-
-        /* Execute every sigp command on the queue */
-        while (ec != NULL) {
-                switch (ec->cmd) {
-                case ec_callback_async: {
-                        void (*func)(void *info);
-                        void *info;
-
-                        func = ec->func;
-                        info = ec->info;
-                        atomic_set(&ec->status,ec_executing);
-                        (func)(info);
-                        return;
-                }
-                case ec_callback_sync:
-                        atomic_set(&ec->status,ec_executing);
-                        (ec->func)(ec->info);
-                        atomic_set(&ec->status,ec_done);
-                        return;
-                default:
-                }
-                ec = ec->next;
-        }
-}
-
-/*
- * Swap in a new request to external call queue 
- */
-static inline void smp_add_ext_call(ec_ext_call *ec, struct _lowcore *lowcore)
-{
-	int success;
-
-	while (1) {
-		ec->next = (ec_ext_call*) lowcore->ext_call_queue;
-		__asm__ __volatile__ (
-                        "   lgr 0,%2\n"
-			"   csg 0,%3,%1\n"
-			"   ipm %0\n"
-			"   srl %0,28\n"
-			: "=d" (success), "+m" (lowcore->ext_call_queue)
-			: "d" (ec->next), "d" (ec)
-			: "cc", "0" );
-		if (success == 0) break;
-	}
-}
-
-/*
- * Send an external call sigp to another cpu and wait for its completion.
- */
-sigp_ccode
-smp_ext_call(int cpu, void (*func)(void *info), void *info, int wait)
-{
-        sigp_ccode ccode;
-        ec_ext_call ec;
-
-        ec.cmd = wait ? ec_callback_sync:ec_callback_async;
-        atomic_set(&ec.status, ec_pending);
-        ec.func = func;
-	ec.info = info;
-	/* swap in new request to external call queue */
-	smp_add_ext_call(&ec, &get_cpu_lowcore(cpu));
-        /*
-         * We try once to deliver the signal. There are four possible
-         * return codes:
-         * 0) Order code accepted - can't show up on an external call
-         * 1) Status stored - fine, wait for completion.
-         * 2) Busy - there is another signal pending. Thats fine too, because
-         *    do_ext_call from the pending signal will execute all signals on
-         *    the queue. We wait for completion.
-         * 3) Not operational - something very bad has happened to the cpu.
-         *    do not wait for completion.
-         */
-        ccode = signal_processor(cpu, sigp_external_call);
-
-        if (ccode != sigp_not_operational)
-                /* wait for completion, FIXME: possible seed of a deadlock */
-                while (atomic_read(&ec.status) != (wait?ec_done:ec_executing));
-
-        return ccode;
-}
-
-/*
- * Send a callback sigp to every other cpu in the system.
- */
-void smp_ext_call_others(void (*func)(void *info), void *info, int wait)
-{
-        ec_ext_call ec[NR_CPUS];
-        sigp_ccode ccode;
-        int i;
-
-        for (i = 0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
-                        continue;
-		ec[i].cmd = wait ? ec_callback_sync : ec_callback_async;
-		atomic_set(&ec[i].status, ec_pending);
-		ec[i].func = func;
-		ec[i].info = info;
-		smp_add_ext_call(ec+i, &get_cpu_lowcore(i));
-                ccode = signal_processor(i, sigp_external_call);
-        }
-
-        /* wait for completion, FIXME: possible seed of a deadlock */
-        for (i = 0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
-                        continue;
-                while (atomic_read(&ec[i].status) != 
-		       (wait ? ec_done:ec_executing));
-        }
+        if (test_bit(ec_call_function, &bits))
+		do_call_function();
 }
 
 /*
  * Send an external call sigp to another cpu and return without waiting
  * for its completion.
  */
-sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
+static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
 {
         sigp_ccode ccode;
 
@@ -314,7 +281,7 @@
  * Send an external call sigp to every other cpu in the system and
  * return without waiting for its completion.
  */
-void smp_ext_bitcall_others(ec_bit_sig sig)
+static void smp_ext_bitcall_others(ec_bit_sig sig)
 {
         sigp_ccode ccode;
         int i;
@@ -331,51 +298,6 @@
 }
 
 /*
- * cycles through all the cpus,
- * returns early if info is not NULL & the processor has something
- * of intrest to report in the info structure.
- * it returns the next cpu to check if it returns early.
- * i.e. it should be used as follows if you wish to receive info.
- * next_cpu=0;
- * do
- * {
- *    info->cpu=next_cpu;
- *    next_cpu=smp_signal_others(order_code,parameter,1,info);
- *    ... check info here
- * } while(next_cpu<=smp_num_cpus)
- *
- *  if you are lazy just use it like
- * smp_signal_others(order_code,parameter,0,1,NULL);
- */
-int smp_signal_others(sigp_order_code order_code, u32 parameter,
-                      int spin, sigp_info *info)
-{
-        sigp_ccode   ccode;
-        u32          dummy;
-        u16          i;
-
-        if (info)
-                info->intresting = 0;
-        for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) {
-                if (smp_processor_id() != i) {
-                        do {
-                                ccode = signal_processor_ps(
-                                        (info ? &info->status : &dummy),
-                                        parameter, i, order_code);
-                        } while(spin && ccode == sigp_busy);
-                        if (info && ccode != sigp_order_code_accepted) {
-                                info->intresting = 1;
-                                info->cpu = i;
-                                info->ccode = ccode;
-                                i++;
-                                break;
-                        }
-                }
-        }
-        return i;
-}
-
-/*
  * this function sends a 'stop' sigp to all other CPUs in the system.
  * it goes straight through.
  */
@@ -392,7 +314,18 @@
 
         /* stop all processors */
 
-        smp_signal_others(sigp_stop, 0, 1, NULL);
+        for (i =  0; i < smp_num_cpus; i++) {
+                if (smp_processor_id() != i) {
+                        int ccode;
+                        do {
+                                ccode = signal_processor_ps(
+                                   &dummy,
+                                   0,
+                                   i,
+                                   sigp_stop);
+                        } while(ccode == sigp_busy);
+                }
+        }
 
         /* store status of all processors in their lowcores (real 0) */
 
@@ -469,7 +402,7 @@
                 parms.end_ctl = cr;
                 parms.orvals[cr] = 1 << bit;
                 parms.andvals[cr] = -1L;
-                smp_ext_call_others(smp_ctl_bit_callback, &parms, 1);
+                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         }
         __ctl_set_bit(cr, bit);
 }
@@ -485,34 +418,11 @@
                 parms.end_ctl = cr;
                 parms.orvals[cr] = 0;
                 parms.andvals[cr] = ~(1L << bit);
-                smp_ext_call_others(smp_ctl_bit_callback, &parms, 1);
+                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         }
         __ctl_clear_bit(cr, bit);
 }
 
-/*
- * Call a function on all other processors
- */
-
-int
-smp_call_function(void (*func)(void *info), void *info, int retry, int wait)
-/*
- * [SUMMARY] Run a function on all other CPUs.
- * <func> The function to run. This must be fast and non-blocking.
- * <info> An arbitrary pointer to pass to the function.
- * <retry> currently unused.
- * <wait> If true, wait (atomically) until function has completed on other CPUs.
- * [RETURNS] 0 on success, else a negative status code. Does not return until
- * remote CPUs are nearly ready to execute <<func>> or are or have executed.
- *
- * You must not call this function with disabled interrupts or from a
- * hardware interrupt handler, you may call it from a bottom half handler.
- */
-{
-        if (atomic_read(&smp_commenced) != 0)
-                smp_ext_call_others(func, info, wait);
-        return 0;
-}
 
 /*
  * Lets check how many CPUs we have.
@@ -607,15 +517,20 @@
         init_tasks[cpu] = idle;
 
         cpu_lowcore=&get_cpu_lowcore(cpu);
-        cpu_lowcore->kernel_stack=idle->thread.ksp;
-        __asm__ __volatile__("stctg 0,15,%0\n\t"
-                             "stam  0,15,%1"
+	cpu_lowcore->save_area[15] = idle->thread.ksp;
+	cpu_lowcore->kernel_stack = (idle->thread.ksp | 16383) + 1;
+        __asm__ __volatile__("la    1,%0\n\t"
+			     "stctg 0,15,0(1)\n\t"
+			     "la    1,%1\n\t"
+                             "stam  0,15,0(1)"
                              : "=m" (cpu_lowcore->cregs_save_area[0]),
                                "=m" (cpu_lowcore->access_regs_save_area[0])
-                             : : "memory");
+                             : : "1", "memory");
 
         eieio();
         signal_processor(cpu,sigp_restart);
+	/* Mark this cpu as online. */
+	set_bit(cpu, &cpu_online_map);
 }
 
 /*
@@ -643,6 +558,7 @@
 void __init smp_boot_cpus(void)
 {
         struct _lowcore *curr_lowcore;
+	unsigned long async_stack;
         sigp_ccode   ccode;
         int i;
 
@@ -673,8 +589,16 @@
                         printk("smp_boot_cpus failed to allocate prefix memory\n");
                         break;
                 }
+		async_stack = __get_free_pages(GFP_KERNEL,2);
+		if (async_stack == 0) {
+			printk("smp_boot_cpus failed to allocate asyncronous"
+			       " interrupt stack\n");
+			free_page((unsigned long) curr_lowcore);
+			break;
+		}
                 lowcore_ptr[i] = curr_lowcore;
                 memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore));
+		curr_lowcore->async_stack = async_stack + (4 * PAGE_SIZE);
                 /*
                  * Most of the parameters are set up when the cpu is
                  * started up.
@@ -733,8 +657,6 @@
                 s390_do_profile(regs->psw.addr);
 
         if (!--prof_counter[cpu]) {
-                int system = 1-user;
-                struct task_struct * p = current;
 
                 /*
                  * The multiplier may have changed since the last time we got
@@ -756,9 +678,7 @@
                  * WrongThing (tm) to do.
                  */
 
-                irq_enter(cpu, 0);
 		update_process_times(user);
-                irq_exit(cpu, 0);
         }
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)