diff options
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/Makefile | 4 | ||||
-rw-r--r-- | drivers/s390/cio/qdio.h | 27 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 57 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_setup.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 41 | ||||
-rw-r--r-- | drivers/s390/cio/vfio_ccw_cp.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/vfio_ccw_fsm.c | 11 | ||||
-rw-r--r-- | drivers/s390/cio/vfio_ccw_private.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/vfio_ccw_trace.c | 14 | ||||
-rw-r--r-- | drivers/s390/cio/vfio_ccw_trace.h | 76 |
10 files changed, 145 insertions, 89 deletions
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index f6a8db04177c..23eae4188876 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -5,7 +5,7 @@ # The following is required for define_trace.h to find ./trace.h CFLAGS_trace.o := -I$(src) -CFLAGS_vfio_ccw_fsm.o := -I$(src) +CFLAGS_vfio_ccw_trace.o := -I$(src) obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o @@ -21,5 +21,5 @@ qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o obj-$(CONFIG_QDIO) += qdio.o vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o \ - vfio_ccw_async.o + vfio_ccw_async.o vfio_ccw_trace.o obj-$(CONFIG_VFIO_CCW) += vfio_ccw.o diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index a58b45df95d7..4b0798472643 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -82,6 +82,7 @@ enum qdio_irq_states { #define QDIO_SIGA_WRITE 0x00 #define QDIO_SIGA_READ 0x01 #define QDIO_SIGA_SYNC 0x02 +#define QDIO_SIGA_WRITEM 0x03 #define QDIO_SIGA_WRITEQ 0x04 #define QDIO_SIGA_QEBSM_FLAG 0x80 @@ -252,9 +253,6 @@ struct qdio_q { /* input or output queue */ int is_input_q; - /* list of thinint input queues */ - struct list_head entry; - /* upper-layer program handler */ qdio_handler_t (*handler); @@ -272,6 +270,7 @@ struct qdio_irq { struct qib qib; u32 *dsci; /* address of device state change indicator */ struct ccw_device *cdev; + struct list_head entry; /* list of thinint devices */ struct dentry *debugfs_dev; struct dentry *debugfs_perf; @@ -317,13 +316,15 @@ struct qdio_irq { #define qperf(__qdev, __attr) ((__qdev)->perf_stat.(__attr)) -#define qperf_inc(__q, __attr) \ +#define QDIO_PERF_STAT_INC(__irq, __attr) \ ({ \ - struct qdio_irq *qdev = (__q)->irq_ptr; \ + struct qdio_irq *qdev = __irq; \ if (qdev->perf_stat_enabled) \ (qdev->perf_stat.__attr)++; \ }) +#define qperf_inc(__q, __attr) QDIO_PERF_STAT_INC((__q)->irq_ptr, __attr) + static inline void account_sbals_error(struct qdio_q *q, int count) { q->q_stats.nr_sbal_error += count; @@ -355,14 +356,10 @@ static inline int multicast_outbound(struct qdio_q *q) for (i = 0; i < irq_ptr->nr_output_qs && \ ({ q = irq_ptr->output_qs[i]; 1; }); i++) -#define prev_buf(bufnr) \ - ((bufnr + QDIO_MAX_BUFFERS_MASK) & QDIO_MAX_BUFFERS_MASK) -#define next_buf(bufnr) \ - ((bufnr + 1) & QDIO_MAX_BUFFERS_MASK) -#define add_buf(bufnr, inc) \ - ((bufnr + inc) & QDIO_MAX_BUFFERS_MASK) -#define sub_buf(bufnr, dec) \ - ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK) +#define add_buf(bufnr, inc) QDIO_BUFNR((bufnr) + (inc)) +#define next_buf(bufnr) add_buf(bufnr, 1) +#define sub_buf(bufnr, dec) QDIO_BUFNR((bufnr) - (dec)) +#define prev_buf(bufnr) sub_buf(bufnr, 1) #define queue_irqs_enabled(q) \ (test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) == 0) @@ -375,8 +372,8 @@ extern u64 last_ai_time; void qdio_setup_thinint(struct qdio_irq *irq_ptr); int qdio_establish_thinint(struct qdio_irq *irq_ptr); void qdio_shutdown_thinint(struct qdio_irq *irq_ptr); -void tiqdio_add_input_queues(struct qdio_irq *irq_ptr); -void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr); +void tiqdio_add_device(struct qdio_irq *irq_ptr); +void tiqdio_remove_device(struct qdio_irq *irq_ptr); void tiqdio_inbound_processing(unsigned long q); int tiqdio_allocate_memory(void); void tiqdio_free_memory(void); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5b63c505a2f7..f8b897b7e78b 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -131,7 +131,7 @@ again: case 96: /* not all buffers processed */ qperf_inc(q, eqbs_partial); - DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x", + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "EQBS part:%02x", tmp_count); return count - tmp_count; case 97: @@ -310,18 +310,19 @@ static inline int qdio_siga_sync_q(struct qdio_q *q) return qdio_siga_sync(q, q->mask, 0); } -static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, - unsigned long aob) +static int qdio_siga_output(struct qdio_q *q, unsigned int count, + unsigned int *busy_bit, unsigned long aob) { unsigned long schid = *((u32 *) &q->irq_ptr->schid); unsigned int fc = QDIO_SIGA_WRITE; u64 start_time = 0; int retries = 0, cc; - unsigned long laob = 0; - if (aob) { - fc = QDIO_SIGA_WRITEQ; - laob = aob; + if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q)) { + if (count > 1) + fc = QDIO_SIGA_WRITEM; + else if (aob) + fc = QDIO_SIGA_WRITEQ; } if (is_qebsm(q)) { @@ -329,7 +330,7 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, fc |= QDIO_SIGA_QEBSM_FLAG; } again: - cc = do_siga_output(schid, q->mask, busy_bit, fc, laob); + cc = do_siga_output(schid, q->mask, busy_bit, fc, aob); /* hipersocket busy condition */ if (unlikely(*busy_bit)) { @@ -423,9 +424,6 @@ static inline void account_sbals(struct qdio_q *q, unsigned int count) static void process_buffer_error(struct qdio_q *q, unsigned int start, int count) { - unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT : - SLSB_P_OUTPUT_NOT_INIT; - q->qdio_error = QDIO_ERROR_SLSB_STATE; /* special handling for no target buffer empty */ @@ -433,7 +431,7 @@ static void process_buffer_error(struct qdio_q *q, unsigned int start, q->sbal[start]->element[15].sflags == 0x10) { qperf_inc(q, target_full); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x", start); - goto set; + return; } DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); @@ -442,13 +440,6 @@ static void process_buffer_error(struct qdio_q *q, unsigned int start, DBF_ERROR("F14:%2x F15:%2x", q->sbal[start]->element[14].sflags, q->sbal[start]->element[15].sflags); - -set: - /* - * Interrupts may be avoided as long as the error is present - * so change the buffer state immediately to avoid starvation. - */ - set_buf_states(q, start, state, count); } static inline void inbound_primed(struct qdio_q *q, unsigned int start, @@ -530,6 +521,11 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) return count; case SLSB_P_INPUT_ERROR: process_buffer_error(q, start, count); + /* + * Interrupts may be avoided as long as the error is present + * so change the buffer state immediately to avoid starvation. + */ + set_buf_states(q, start, SLSB_P_INPUT_NOT_INIT, count); if (atomic_sub_return(count, &q->nr_buf_used) == 0) qperf_inc(q, inbound_queue_full); if (q->irq_ptr->perf_stat_enabled) @@ -781,7 +777,8 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start) return count; } -static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob) +static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count, + unsigned long aob) { int retries = 0, cc; unsigned int busy_bit; @@ -793,7 +790,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob) retry: qperf_inc(q, siga_write); - cc = qdio_siga_output(q, &busy_bit, aob); + cc = qdio_siga_output(q, count, &busy_bit, aob); switch (cc) { case 0: break; @@ -963,7 +960,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state)) { - qperf_inc(q, int_discarded); + QDIO_PERF_STAT_INC(irq_ptr, int_discarded); continue; } q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, @@ -1162,7 +1159,7 @@ int qdio_shutdown(struct ccw_device *cdev, int how) */ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); - tiqdio_remove_input_queues(irq_ptr); + tiqdio_remove_device(irq_ptr); qdio_shutdown_queues(cdev); qdio_shutdown_debug_entries(irq_ptr); @@ -1284,6 +1281,7 @@ int qdio_allocate(struct qdio_initialize *init_data) init_data->no_output_qs)) goto out_rel; + INIT_LIST_HEAD(&irq_ptr->entry); init_data->cdev->private->qdio_data = irq_ptr; qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); return 0; @@ -1428,7 +1426,7 @@ int qdio_activate(struct ccw_device *cdev) } if (is_thinint_irq(irq_ptr)) - tiqdio_add_input_queues(irq_ptr); + tiqdio_add_device(irq_ptr); /* wait for subchannel to become active */ msleep(5); @@ -1526,7 +1524,7 @@ set: * @count: how many buffers are filled */ static int handle_outbound(struct qdio_q *q, unsigned int callflags, - int bufnr, int count) + unsigned int bufnr, unsigned int count) { const unsigned int scan_threshold = q->irq_ptr->scan_threshold; unsigned char state = 0; @@ -1549,13 +1547,10 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, if (queue_type(q) == QDIO_IQDIO_QFMT) { unsigned long phys_aob = 0; - /* One SIGA-W per buffer required for unicast HSI */ - WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); - - if (q->u.out.use_cq) + if (q->u.out.use_cq && count == 1) phys_aob = qdio_aob_for_buffer(&q->u.out, bufnr); - rc = qdio_kick_outbound_q(q, phys_aob); + rc = qdio_kick_outbound_q(q, count, phys_aob); } else if (need_siga_sync(q)) { rc = qdio_siga_sync_q(q); } else if (count < QDIO_MAX_BUFFERS_PER_Q && @@ -1564,7 +1559,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, /* The previous buffer is not processed yet, tack on. */ qperf_inc(q, fast_requeue); } else { - rc = qdio_kick_outbound_q(q, 0); + rc = qdio_kick_outbound_q(q, count, 0); } /* Let drivers implement their own completion scanning: */ diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index cd164886132f..dc430bd86ade 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -150,7 +150,6 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues) return -ENOMEM; } irq_ptr_qs[i] = q; - INIT_LIST_HEAD(&q->entry); } return 0; } @@ -179,7 +178,6 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, q->mask = 1 << (31 - i); q->nr = i; q->handler = handler; - INIT_LIST_HEAD(&q->entry); } static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 93ee067c10ca..7c4e4ec08a12 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -39,14 +39,6 @@ struct indicator_t { static LIST_HEAD(tiq_list); static DEFINE_MUTEX(tiq_list_lock); -/* Adapter interrupt definitions */ -static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating); - -static struct airq_struct tiqdio_airq = { - .handler = tiqdio_thinint_handler, - .isc = QDIO_AIRQ_ISC, -}; - static struct indicator_t *q_indicators; u64 last_ai_time; @@ -74,26 +66,20 @@ static void put_indicator(u32 *addr) atomic_dec(&ind->count); } -void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) +void tiqdio_add_device(struct qdio_irq *irq_ptr) { mutex_lock(&tiq_list_lock); - list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list); + list_add_rcu(&irq_ptr->entry, &tiq_list); mutex_unlock(&tiq_list_lock); } -void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) +void tiqdio_remove_device(struct qdio_irq *irq_ptr) { - struct qdio_q *q; - - q = irq_ptr->input_qs[0]; - if (!q) - return; - mutex_lock(&tiq_list_lock); - list_del_rcu(&q->entry); + list_del_rcu(&irq_ptr->entry); mutex_unlock(&tiq_list_lock); synchronize_rcu(); - INIT_LIST_HEAD(&q->entry); + INIT_LIST_HEAD(&irq_ptr->entry); } static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr) @@ -154,7 +140,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state)) { - qperf_inc(q, int_discarded); + QDIO_PERF_STAT_INC(irq, int_discarded); continue; } @@ -182,7 +168,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) { u32 si_used = clear_shared_ind(); - struct qdio_q *q; + struct qdio_irq *irq; last_ai_time = S390_lowcore.int_clock; inc_irq_stat(IRQIO_QAI); @@ -190,12 +176,8 @@ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) /* protect tiq_list entries, only changed in activate or shutdown */ rcu_read_lock(); - /* check for work on all inbound thinint queues */ - list_for_each_entry_rcu(q, &tiq_list, entry) { - struct qdio_irq *irq; - + list_for_each_entry_rcu(irq, &tiq_list, entry) { /* only process queues from changed sets */ - irq = q->irq_ptr; if (unlikely(references_shared_dsci(irq))) { if (!si_used) continue; @@ -204,11 +186,16 @@ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) tiqdio_call_inq_handlers(irq); - qperf_inc(q, adapter_int); + QDIO_PERF_STAT_INC(irq, adapter_int); } rcu_read_unlock(); } +static struct airq_struct tiqdio_airq = { + .handler = tiqdio_thinint_handler, + .isc = QDIO_AIRQ_ISC, +}; + static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) { struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page; diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index 7cdc38049033..ba31240ce965 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -15,6 +15,7 @@ #include <asm/scsw.h> #include "orb.h" +#include "vfio_ccw_trace.h" /* * Max length for ccw chain. diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 4a1e727c62d9..23e61aa638e4 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -15,9 +15,6 @@ #include "ioasm.h" #include "vfio_ccw_private.h" -#define CREATE_TRACE_POINTS -#include "vfio_ccw_trace.h" - static int fsm_io_helper(struct vfio_ccw_private *private) { struct subchannel *sch; @@ -321,8 +318,8 @@ static void fsm_io_request(struct vfio_ccw_private *private, } err_out: - trace_vfio_ccw_io_fctl(scsw->cmd.fctl, schid, - io_region->ret_code, errstr); + trace_vfio_ccw_fsm_io_request(scsw->cmd.fctl, schid, + io_region->ret_code, errstr); } /* @@ -344,6 +341,10 @@ static void fsm_async_request(struct vfio_ccw_private *private, /* should not happen? */ cmd_region->ret_code = -EINVAL; } + + trace_vfio_ccw_fsm_async_request(get_schid(private), + cmd_region->command, + cmd_region->ret_code); } /* diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index bbe9babf767b..9b9bb4982972 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -135,6 +135,7 @@ extern fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS]; static inline void vfio_ccw_fsm_event(struct vfio_ccw_private *private, int event) { + trace_vfio_ccw_fsm_event(private->sch->schid, private->state, event); vfio_ccw_jumptable[private->state][event](private, event); } diff --git a/drivers/s390/cio/vfio_ccw_trace.c b/drivers/s390/cio/vfio_ccw_trace.c new file mode 100644 index 000000000000..8c671d2519f6 --- /dev/null +++ b/drivers/s390/cio/vfio_ccw_trace.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tracepoint definitions for vfio_ccw + * + * Copyright IBM Corp. 2019 + * Author(s): Eric Farman <farman@linux.ibm.com> + */ + +#define CREATE_TRACE_POINTS +#include "vfio_ccw_trace.h" + +EXPORT_TRACEPOINT_SYMBOL(vfio_ccw_fsm_async_request); +EXPORT_TRACEPOINT_SYMBOL(vfio_ccw_fsm_event); +EXPORT_TRACEPOINT_SYMBOL(vfio_ccw_fsm_io_request); diff --git a/drivers/s390/cio/vfio_ccw_trace.h b/drivers/s390/cio/vfio_ccw_trace.h index b1da53ddec1f..30162a318a8a 100644 --- a/drivers/s390/cio/vfio_ccw_trace.h +++ b/drivers/s390/cio/vfio_ccw_trace.h @@ -7,6 +7,8 @@ * Halil Pasic <pasic@linux.vnet.ibm.com> */ +#include "cio.h" + #undef TRACE_SYSTEM #define TRACE_SYSTEM vfio_ccw @@ -15,28 +17,88 @@ #include <linux/tracepoint.h> -TRACE_EVENT(vfio_ccw_io_fctl, +TRACE_EVENT(vfio_ccw_fsm_async_request, + TP_PROTO(struct subchannel_id schid, + int command, + int errno), + TP_ARGS(schid, command, errno), + + TP_STRUCT__entry( + __field(u8, cssid) + __field(u8, ssid) + __field(u16, sch_no) + __field(int, command) + __field(int, errno) + ), + + TP_fast_assign( + __entry->cssid = schid.cssid; + __entry->ssid = schid.ssid; + __entry->sch_no = schid.sch_no; + __entry->command = command; + __entry->errno = errno; + ), + + TP_printk("schid=%x.%x.%04x command=0x%x errno=%d", + __entry->cssid, + __entry->ssid, + __entry->sch_no, + __entry->command, + __entry->errno) +); + +TRACE_EVENT(vfio_ccw_fsm_event, + TP_PROTO(struct subchannel_id schid, int state, int event), + TP_ARGS(schid, state, event), + + TP_STRUCT__entry( + __field(u8, cssid) + __field(u8, ssid) + __field(u16, schno) + __field(int, state) + __field(int, event) + ), + + TP_fast_assign( + __entry->cssid = schid.cssid; + __entry->ssid = schid.ssid; + __entry->schno = schid.sch_no; + __entry->state = state; + __entry->event = event; + ), + + TP_printk("schid=%x.%x.%04x state=%d event=%d", + __entry->cssid, __entry->ssid, __entry->schno, + __entry->state, + __entry->event) +); + +TRACE_EVENT(vfio_ccw_fsm_io_request, TP_PROTO(int fctl, struct subchannel_id schid, int errno, char *errstr), TP_ARGS(fctl, schid, errno, errstr), TP_STRUCT__entry( + __field(u8, cssid) + __field(u8, ssid) + __field(u16, sch_no) __field(int, fctl) - __field_struct(struct subchannel_id, schid) __field(int, errno) __field(char*, errstr) ), TP_fast_assign( + __entry->cssid = schid.cssid; + __entry->ssid = schid.ssid; + __entry->sch_no = schid.sch_no; __entry->fctl = fctl; - __entry->schid = schid; __entry->errno = errno; __entry->errstr = errstr; ), - TP_printk("schid=%x.%x.%04x fctl=%x errno=%d info=%s", - __entry->schid.cssid, - __entry->schid.ssid, - __entry->schid.sch_no, + TP_printk("schid=%x.%x.%04x fctl=0x%x errno=%d info=%s", + __entry->cssid, + __entry->ssid, + __entry->sch_no, __entry->fctl, __entry->errno, __entry->errstr) |