From 8b646bd759086f6090fe27acf414c0b5faa737f4 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sun, 11 Mar 2012 11:59:26 -0400 Subject: [S390] rework smp code Define struct pcpu and merge some of the NR_CPUS arrays into it, including __cpu_logical_map, current_set and smp_cpu_state. Split smp related functions to those operating on physical cpus and the functions operating on a logical cpu number. Make the functions for physical cpus use a pointer to a struct pcpu. This hides the knowledge about cpu addresses in smp.c, entry[64].S and swsusp_asm64.S, thus remove the sigp.h header. The PSW restart mechanism is used to start secondary cpus, calling a function on an online cpu, calling a function on the ipl cpu, and for the nmi signal. Replace the different assembler functions with a single function restart_int_handler. The new entry point calls a function whose pointer is stored in the lowcore of the target cpu and it can wait for the source cpu to stop. This covers all existing use cases. Overall the code is now simpler and there are ~380 lines less code. Reviewed-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_quiesce.c | 1 - drivers/s390/char/zcore.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/s390/char') diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index 87fc0ac11e67..69df137310bc 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "sclp.h" diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 1b6d9247fdc7..3303d66b2794 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-55-g7522 From e3c652b533aab733bf3ffb50654fb82f079c5329 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Sun, 11 Mar 2012 11:59:29 -0400 Subject: [S390] zfcpdump: Implement async sdias event processing There are two different ways to do the "store data in absolute storage" SCLP event processing, synchronous and asynchrounous. This patch adds the asynchronous variant. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_sdias.c | 101 ++++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 21 deletions(-) (limited to 'drivers/s390/char') diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index fa733ecd3d70..69e6c50d4cfb 100644 --- a/drivers/s390/char/sclp_sdias.c +++ b/drivers/s390/char/sclp_sdias.c @@ -8,6 +8,7 @@ #define KMSG_COMPONENT "sclp_sdias" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -62,15 +63,29 @@ struct sdias_sccb { } __attribute__((packed)); static struct sdias_sccb sccb __attribute__((aligned(4096))); +static struct sdias_evbuf sdias_evbuf; -static int sclp_req_done; -static wait_queue_head_t sdias_wq; +static DECLARE_COMPLETION(evbuf_accepted); +static DECLARE_COMPLETION(evbuf_done); static DEFINE_MUTEX(sdias_mutex); +/* + * Called by SCLP base when read event data has been completed (async mode only) + */ +static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf) +{ + memcpy(&sdias_evbuf, evbuf, + min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length)); + complete(&evbuf_done); + TRACE("sclp_sdias_receiver_fn done\n"); +} + +/* + * Called by SCLP base when sdias event has been accepted + */ static void sdias_callback(struct sclp_req *request, void *data) { - sclp_req_done = 1; - wake_up(&sdias_wq); /* Inform caller, that request is complete */ + complete(&evbuf_accepted); TRACE("callback done\n"); } @@ -80,7 +95,6 @@ static int sdias_sclp_send(struct sclp_req *req) int rc; for (retries = SDIAS_RETRIES; retries; retries--) { - sclp_req_done = 0; TRACE("add request\n"); rc = sclp_add_request(req); if (rc) { @@ -91,16 +105,31 @@ static int sdias_sclp_send(struct sclp_req *req) continue; } /* initiated, wait for completion of service call */ - wait_event(sdias_wq, (sclp_req_done == 1)); + wait_for_completion(&evbuf_accepted); if (req->status == SCLP_REQ_FAILED) { TRACE("sclp request failed\n"); - rc = -EIO; continue; } + /* if not accepted, retry */ + if (!(sccb.evbuf.hdr.flags & 0x80)) { + TRACE("sclp request failed: flags=%x\n", + sccb.evbuf.hdr.flags); + continue; + } + /* + * for the sync interface the response is in the initial sccb + */ + if (!sclp_sdias_register.receiver_fn) { + memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf)); + TRACE("sync request done\n"); + return 0; + } + /* otherwise we wait for completion */ + wait_for_completion(&evbuf_done); TRACE("request done\n"); - break; + return 0; } - return rc; + return -EIO; } /* @@ -140,13 +169,12 @@ int sclp_sdias_blk_count(void) goto out; } - switch (sccb.evbuf.event_status) { + switch (sdias_evbuf.event_status) { case 0: - rc = sccb.evbuf.blk_cnt; + rc = sdias_evbuf.blk_cnt; break; default: - pr_err("SCLP error: %x\n", - sccb.evbuf.event_status); + pr_err("SCLP error: %x\n", sdias_evbuf.event_status); rc = -EIO; goto out; } @@ -211,18 +239,18 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) goto out; } - switch (sccb.evbuf.event_status) { + switch (sdias_evbuf.event_status) { case EVSTATE_ALL_STORED: TRACE("all stored\n"); case EVSTATE_PART_STORED: - TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); + TRACE("part stored: %i\n", sdias_evbuf.blk_cnt); break; case EVSTATE_NO_DATA: TRACE("no data\n"); default: pr_err("Error from SCLP while copying hsa. " "Event status = %x\n", - sccb.evbuf.event_status); + sdias_evbuf.event_status); rc = -EIO; } out: @@ -230,19 +258,50 @@ out: return rc; } -int __init sclp_sdias_init(void) +static int __init sclp_sdias_register_check(void) { int rc; + rc = sclp_register(&sclp_sdias_register); + if (rc) + return rc; + if (sclp_sdias_blk_count() == 0) { + sclp_unregister(&sclp_sdias_register); + return -ENODEV; + } + return 0; +} + +static int __init sclp_sdias_init_sync(void) +{ + TRACE("Try synchronous mode\n"); + sclp_sdias_register.receive_mask = 0; + sclp_sdias_register.receiver_fn = NULL; + return sclp_sdias_register_check(); +} + +static int __init sclp_sdias_init_async(void) +{ + TRACE("Try asynchronous mode\n"); + sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK; + sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn; + return sclp_sdias_register_check(); +} + +int __init sclp_sdias_init(void) +{ if (ipl_info.type != IPL_TYPE_FCP_DUMP) return 0; sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); debug_register_view(sdias_dbf, &debug_sprintf_view); debug_set_level(sdias_dbf, 6); - rc = sclp_register(&sclp_sdias_register); - if (rc) - return rc; - init_waitqueue_head(&sdias_wq); + if (sclp_sdias_init_sync() == 0) + goto out; + if (sclp_sdias_init_async() == 0) + goto out; + TRACE("init failed\n"); + return -ENODEV; +out: TRACE("init done\n"); return 0; } -- cgit v1.2.3-55-g7522 From fde15c3a3adc7b65cd0610dd6bca4804ee7ffd38 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 11 Mar 2012 11:59:31 -0400 Subject: [S390] irq: external interrupt code passing The external interrupt handlers have a parameter called ext_int_code. Besides the name this paramter does not only contain the ext_int_code but in addition also the "cpu address" (POP) which caused the external interrupt. To make the code a bit more obvious pass a struct instead so the called function can easily distinguish between external interrupt code and cpu address. The cpu address field however is named "subcode" since some external interrupt sources do not pass a cpu address but a different parameter (or none at all). Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 7 ++++++- arch/s390/kernel/entry.h | 3 ++- arch/s390/kernel/irq.c | 12 +++++------- arch/s390/kernel/smp.c | 4 ++-- arch/s390/kernel/time.c | 4 ++-- arch/s390/kernel/vtime.c | 2 +- arch/s390/mm/fault.c | 4 ++-- arch/s390/oprofile/hwsampler.c | 6 +++--- drivers/s390/block/dasd_diag.c | 8 ++++---- drivers/s390/char/sclp.c | 4 ++-- drivers/s390/kvm/kvm_virtio.c | 6 ++---- net/iucv/iucv.c | 2 +- 12 files changed, 32 insertions(+), 30 deletions(-) (limited to 'drivers/s390/char') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index ba6d85f88d50..acee1806f61e 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -34,7 +34,12 @@ enum interruption_class { NR_IRQS, }; -typedef void (*ext_int_handler_t)(unsigned int, unsigned int, unsigned long); +struct ext_code { + unsigned short subcode; + unsigned short code; +}; + +typedef void (*ext_int_handler_t)(struct ext_code, unsigned int, unsigned long); int register_external_interrupt(u16 code, ext_int_handler_t handler); int unregister_external_interrupt(u16 code, ext_int_handler_t handler); diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 4984785e3078..6cdddac93a2e 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -35,7 +35,8 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); void do_notify_resume(struct pt_regs *regs); -void do_extint(struct pt_regs *regs, unsigned int, unsigned int, unsigned long); +struct ext_code; +void do_extint(struct pt_regs *regs, struct ext_code, unsigned int, unsigned long); void do_restart(void); void __init startup_init(void); void die(struct pt_regs *regs, const char *str); diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 09a014c62537..7a97d57f7073 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -209,29 +209,27 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler) } EXPORT_SYMBOL(unregister_external_interrupt); -void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code, +void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct pt_regs *old_regs; - unsigned short code; struct ext_int_info *p; int index; - code = (unsigned short) ext_int_code; old_regs = set_irq_regs(regs); irq_enter(); if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) /* Serve timer interrupts first. */ clock_comparator_work(); kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; - if (code != 0x1004) + if (ext_code.code != 0x1004) __get_cpu_var(s390_idle).nohz_delay = 1; - index = ext_hash(code); + index = ext_hash(ext_code.code); rcu_read_lock(); list_for_each_entry_rcu(p, &ext_int_hash[index], entry) - if (likely(p->code == code)) - p->handler(ext_int_code, param32, param64); + if (likely(p->code == ext_code.code)) + p->handler(ext_code, param32, param64); rcu_read_unlock(); irq_exit(); set_irq_regs(old_regs); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index afd6e5113a90..734e644972ab 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -434,14 +434,14 @@ void smp_stop_cpu(void) * This is the main routine where commands issued by other * cpus are handled. */ -static void do_ext_call_interrupt(unsigned int ext_int_code, +static void do_ext_call_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { unsigned long bits; int cpu; cpu = smp_processor_id(); - if ((ext_int_code & 0xffff) == 0x1202) + if (ext_code.code == 0x1202) kstat_cpu(cpu).irqs[EXTINT_EXC]++; else kstat_cpu(cpu).irqs[EXTINT_EMS]++; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 14da278febbf..d4e1cb1dbcd1 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -165,7 +165,7 @@ void init_cpu_timer(void) __ctl_set_bit(0, 4); } -static void clock_comparator_interrupt(unsigned int ext_int_code, +static void clock_comparator_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { @@ -177,7 +177,7 @@ static void clock_comparator_interrupt(unsigned int ext_int_code, static void etr_timing_alert(struct etr_irq_parm *); static void stp_timing_alert(struct stp_irq_parm *); -static void timing_alert_interrupt(unsigned int ext_int_code, +static void timing_alert_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { kstat_cpu(smp_processor_id()).irqs[EXTINT_TLA]++; diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 277ea712b232..39ebff506946 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -220,7 +220,7 @@ static void do_callbacks(struct list_head *cb_list) /* * Handler for the virtual CPU timer. */ -static void do_cpu_timer_interrupt(unsigned int ext_int_code, +static void do_cpu_timer_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct vtimer_queue *vq; diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index e8fcd928dc78..b17c42df61c9 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -532,7 +532,7 @@ void pfault_fini(void) static DEFINE_SPINLOCK(pfault_lock); static LIST_HEAD(pfault_list); -static void pfault_interrupt(unsigned int ext_int_code, +static void pfault_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct task_struct *tsk; @@ -545,7 +545,7 @@ static void pfault_interrupt(unsigned int ext_int_code, * in the 'cpu address' field associated with the * external interrupt. */ - subcode = ext_int_code >> 16; + subcode = ext_code.subcode; if ((subcode & 0xff00) != __SUBCODE_MASK) return; kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++; diff --git a/arch/s390/oprofile/hwsampler.c b/arch/s390/oprofile/hwsampler.c index 9daee91e6c3f..12bea05a0fc1 100644 --- a/arch/s390/oprofile/hwsampler.c +++ b/arch/s390/oprofile/hwsampler.c @@ -233,8 +233,8 @@ static inline unsigned long *trailer_entry_ptr(unsigned long v) } /* prototypes for external interrupt handler and worker */ -static void hws_ext_handler(unsigned int ext_int_code, - unsigned int param32, unsigned long param64); +static void hws_ext_handler(struct ext_code ext_code, + unsigned int param32, unsigned long param64); static void worker(struct work_struct *work); @@ -673,7 +673,7 @@ int hwsampler_activate(unsigned int cpu) return rc; } -static void hws_ext_handler(unsigned int ext_int_code, +static void hws_ext_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct hws_cpu_buffer *cb; diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 46784b83c5c4..0cea7e98f464 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -229,7 +229,7 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) } /* Handle external interruption. */ -static void dasd_ext_handler(unsigned int ext_int_code, +static void dasd_ext_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct dasd_ccw_req *cqr, *next; @@ -239,7 +239,7 @@ static void dasd_ext_handler(unsigned int ext_int_code, addr_t ip; int rc; - switch (ext_int_code >> 24) { + switch (ext_code.subcode >> 8) { case DASD_DIAG_CODE_31BIT: ip = (addr_t) param32; break; @@ -280,7 +280,7 @@ static void dasd_ext_handler(unsigned int ext_int_code, cqr->stopclk = get_clock(); expires = 0; - if ((ext_int_code & 0xff0000) == 0) { + if ((ext_code.subcode & 0xff) == 0) { cqr->status = DASD_CQR_SUCCESS; /* Start first request on queue if possible -> fast_io. */ if (!list_empty(&device->ccw_queue)) { @@ -296,7 +296,7 @@ static void dasd_ext_handler(unsigned int ext_int_code, cqr->status = DASD_CQR_QUEUED; DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for " "request %p was %d (%d retries left)", cqr, - (ext_int_code >> 16) & 0xff, cqr->retries); + ext_code.subcode & 0xff, cqr->retries); dasd_diag_erp(device); } diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index eaa7e78186f9..30f29a0020a1 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -393,7 +393,7 @@ __sclp_find_req(u32 sccb) /* Handler for external interruption. Perform request post-processing. * Prepare read event data request if necessary. Start processing of next * request on queue. */ -static void sclp_interrupt_handler(unsigned int ext_int_code, +static void sclp_interrupt_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct sclp_req *req; @@ -818,7 +818,7 @@ EXPORT_SYMBOL(sclp_reactivate); /* Handler for external interruption used during initialization. Modify * request state to done. */ -static void sclp_check_handler(unsigned int ext_int_code, +static void sclp_check_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { u32 finished_sccb; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 7bc1955337ea..d74e9ae6dfb3 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -380,15 +380,13 @@ static void hotplug_devices(struct work_struct *dummy) /* * we emulate the request_irq behaviour on top of s390 extints */ -static void kvm_extint_handler(unsigned int ext_int_code, +static void kvm_extint_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct virtqueue *vq; - u16 subcode; u32 param; - subcode = ext_int_code >> 16; - if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) + if ((ext_code.subcode & 0xff00) != VIRTIO_SUBCODE_64) return; kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++; diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 403be43b793d..3ad1f9db5f8b 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -1800,7 +1800,7 @@ static void iucv_work_fn(struct work_struct *work) * Handles external interrupts coming in from CP. * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn(). */ -static void iucv_external_interrupt(unsigned int ext_int_code, +static void iucv_external_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { struct iucv_irq_data *p; -- cgit v1.2.3-55-g7522