diff options
Diffstat (limited to 'drivers/s390')
37 files changed, 1240 insertions, 768 deletions
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index 5542d9eadfe0..7d079154f849 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -116,7 +116,9 @@ int dasd_scan_partitions(struct dasd_block *block) return -ENODEV; } - rc = blkdev_reread_part(bdev); + mutex_lock(&bdev->bd_mutex); + rc = bdev_disk_changed(bdev, false); + mutex_unlock(&bdev->bd_mutex); if (rc) DBF_DEV_EVENT(DBF_ERR, block->base, "scan partitions error, rc %d", rc); diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index ea4253939555..8abb42923307 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -341,14 +341,14 @@ tapechar_release(struct inode *inode, struct file *filp) */ static int __tapechar_ioctl(struct tape_device *device, - unsigned int no, unsigned long data) + unsigned int no, void __user *data) { int rc; if (no == MTIOCTOP) { struct mtop op; - if (copy_from_user(&op, (char __user *) data, sizeof(op)) != 0) + if (copy_from_user(&op, data, sizeof(op)) != 0) return -EFAULT; if (op.mt_count < 0) return -EINVAL; @@ -392,9 +392,7 @@ __tapechar_ioctl(struct tape_device *device, if (rc < 0) return rc; pos.mt_blkno = rc; - if (copy_to_user((char __user *) data, &pos, sizeof(pos)) != 0) - return -EFAULT; - return 0; + return put_user_mtpos(data, &pos); } if (no == MTIOCGET) { /* MTIOCGET: query the tape drive status. */ @@ -424,15 +422,12 @@ __tapechar_ioctl(struct tape_device *device, get.mt_blkno = rc; } - if (copy_to_user((char __user *) data, &get, sizeof(get)) != 0) - return -EFAULT; - - return 0; + return put_user_mtget(data, &get); } /* Try the discipline ioctl function. */ if (device->discipline->ioctl_fn == NULL) return -EINVAL; - return device->discipline->ioctl_fn(device, no, data); + return device->discipline->ioctl_fn(device, no, (unsigned long)data); } static long @@ -445,7 +440,7 @@ tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data) device = (struct tape_device *) filp->private_data; mutex_lock(&device->mutex); - rc = __tapechar_ioctl(device, no, data); + rc = __tapechar_ioctl(device, no, (void __user *)data); mutex_unlock(&device->mutex); return rc; } @@ -455,23 +450,17 @@ static long tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) { struct tape_device *device = filp->private_data; - int rval = -ENOIOCTLCMD; - unsigned long argp; + long rc; - /* The 'arg' argument of any ioctl function may only be used for - * pointers because of the compat pointer conversion. - * Consider this when adding new ioctls. - */ - argp = (unsigned long) compat_ptr(data); - if (device->discipline->ioctl_fn) { - mutex_lock(&device->mutex); - rval = device->discipline->ioctl_fn(device, no, argp); - mutex_unlock(&device->mutex); - if (rval == -EINVAL) - rval = -ENOIOCTLCMD; - } + if (no == MTIOCPOS32) + no = MTIOCPOS; + else if (no == MTIOCGET32) + no = MTIOCGET; - return rval; + mutex_lock(&device->mutex); + rc = __tapechar_ioctl(device, no, compat_ptr(data)); + mutex_unlock(&device->mutex); + return rc; } #endif /* CONFIG_COMPAT */ 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) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 9de3d46b3253..d78d77686d7b 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -715,36 +715,18 @@ out: static void *_copy_key_from_user(void __user *ukey, size_t keylen) { - void *kkey; - if (!ukey || keylen < MINKEYBLOBSIZE || keylen > KEYBLOBBUFSIZE) return ERR_PTR(-EINVAL); - kkey = kmalloc(keylen, GFP_KERNEL); - if (!kkey) - return ERR_PTR(-ENOMEM); - if (copy_from_user(kkey, ukey, keylen)) { - kfree(kkey); - return ERR_PTR(-EFAULT); - } - return kkey; + return memdup_user(ukey, keylen); } static void *_copy_apqns_from_user(void __user *uapqns, size_t nr_apqns) { - void *kapqns = NULL; - size_t nbytes; - - if (uapqns && nr_apqns > 0) { - nbytes = nr_apqns * sizeof(struct pkey_apqn); - kapqns = kmalloc(nbytes, GFP_KERNEL); - if (!kapqns) - return ERR_PTR(-ENOMEM); - if (copy_from_user(kapqns, uapqns, nbytes)) - return ERR_PTR(-EFAULT); - } + if (!uapqns || nr_apqns == 0) + return NULL; - return kapqns; + return memdup_user(uapqns, nr_apqns * sizeof(struct pkey_apqn)); } static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h index f34ee41cbed8..4f4dd9d727c9 100644 --- a/drivers/s390/crypto/zcrypt_error.h +++ b/drivers/s390/crypto/zcrypt_error.h @@ -61,6 +61,7 @@ struct error_hdr { #define REP82_ERROR_EVEN_MOD_IN_OPND 0x85 #define REP82_ERROR_RESERVED_FIELD 0x88 #define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A +#define REP82_ERROR_FILTERED_BY_HYPERVISOR 0x8B #define REP82_ERROR_TRANSPORT_FAIL 0x90 #define REP82_ERROR_PACKET_TRUNCATED 0xA0 #define REP82_ERROR_ZERO_BUFFER_LEN 0xB0 @@ -91,6 +92,7 @@ static inline int convert_error(struct zcrypt_queue *zq, case REP82_ERROR_INVALID_DOMAIN_PRECHECK: case REP82_ERROR_INVALID_DOMAIN_PENDING: case REP82_ERROR_INVALID_SPECIAL_CMD: + case REP82_ERROR_FILTERED_BY_HYPERVISOR: // REP88_ERROR_INVALID_KEY // '82' CEX2A // REP88_ERROR_OPERAND // '84' CEX2A // REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A diff --git a/drivers/s390/net/ism.h b/drivers/s390/net/ism.h index 66eac2b9704d..1901e9c80ed8 100644 --- a/drivers/s390/net/ism.h +++ b/drivers/s390/net/ism.h @@ -32,8 +32,6 @@ #define ISM_UNREG_SBA 0x11 #define ISM_UNREG_IEQ 0x12 -#define ISM_ERROR 0xFFFF - struct ism_req_hdr { u32 cmd; u16 : 16; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index e4b55f9aa062..871d44746f5c 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -368,6 +368,7 @@ enum qeth_header_ids { QETH_HEADER_TYPE_L3_TSO = 0x03, QETH_HEADER_TYPE_OSN = 0x04, QETH_HEADER_TYPE_L2_TSO = 0x06, + QETH_HEADER_MASK_INVAL = 0x80, }; /* flags for qeth_hdr.ext_flags */ #define QETH_HDR_EXT_VLAN_FRAME 0x01 @@ -477,12 +478,17 @@ struct qeth_card_stats { u64 rx_sg_frags; u64 rx_sg_alloc_page; + u64 rx_dropped_nomem; + u64 rx_dropped_notsupp; + u64 rx_dropped_runt; + /* rtnl_link_stats64 */ u64 rx_packets; u64 rx_bytes; - u64 rx_errors; - u64 rx_dropped; u64 rx_multicast; + u64 rx_length_errors; + u64 rx_frame_errors; + u64 rx_fifo_errors; }; struct qeth_out_q_stats { @@ -532,6 +538,8 @@ struct qeth_qdio_out_q { struct timer_list timer; struct qeth_hdr *prev_hdr; u8 bulk_start; + u8 bulk_count; + u8 bulk_max; }; #define qeth_for_each_output_queue(card, q, i) \ @@ -620,6 +628,7 @@ struct qeth_ipato { struct qeth_channel { struct ccw_device *ccwdev; + struct qeth_cmd_buffer *active_cmd; enum qeth_channel_states state; atomic_t irq_pending; }; @@ -817,7 +826,6 @@ struct qeth_card { struct workqueue_struct *event_wq; struct workqueue_struct *cmd_wq; wait_queue_head_t wait_q; - unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; DECLARE_HASHTABLE(mac_htable, 4); DECLARE_HASHTABLE(ip_htable, 4); struct mutex ip_lock; @@ -839,6 +847,7 @@ struct qeth_card { struct service_level qeth_service_level; struct qdio_ssqd_desc ssqd; debug_info_t *debug; + struct mutex sbp_lock; struct mutex conf_mutex; struct mutex discipline_mutex; struct napi_struct napi; @@ -878,6 +887,13 @@ static inline u16 qeth_iqd_translate_txq(struct net_device *dev, u16 txq) return txq; } +static inline bool qeth_iqd_is_mcast_queue(struct qeth_card *card, + struct qeth_qdio_out_q *queue) +{ + return qeth_iqd_translate_txq(card->dev, queue->queue_no) == + QETH_IQD_MCAST_TXQ; +} + static inline void qeth_scrub_qdio_buffer(struct qdio_buffer *buf, unsigned int elements) { @@ -1023,6 +1039,8 @@ int qeth_do_run_thread(struct qeth_card *, unsigned long); void qeth_clear_thread_start_bit(struct qeth_card *, unsigned long); void qeth_clear_thread_running_bit(struct qeth_card *, unsigned long); int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok); +int qeth_stop_channel(struct qeth_channel *channel); + void qeth_print_status_message(struct qeth_card *); int qeth_init_qdio_queues(struct qeth_card *); int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index dda274351c21..b9a2349e4b90 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -515,7 +515,9 @@ static int __qeth_issue_next_read(struct qeth_card *card) QETH_CARD_TEXT(card, 6, "noirqpnd"); rc = ccw_device_start(channel->ccwdev, ccw, (addr_t) iob, 0, 0); - if (rc) { + if (!rc) { + channel->active_cmd = iob; + } else { QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n", rc, CARD_DEVID(card)); atomic_set(&channel->irq_pending, 0); @@ -901,30 +903,30 @@ static int qeth_get_problem(struct qeth_card *card, struct ccw_device *cdev, CCW_DEVID(cdev), dstat, cstat); print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 16, 1, irb, 64, 1); - return 1; + return -EIO; } if (dstat & DEV_STAT_UNIT_CHECK) { if (sense[SENSE_RESETTING_EVENT_BYTE] & SENSE_RESETTING_EVENT_FLAG) { QETH_CARD_TEXT(card, 2, "REVIND"); - return 1; + return -EIO; } if (sense[SENSE_COMMAND_REJECT_BYTE] & SENSE_COMMAND_REJECT_FLAG) { QETH_CARD_TEXT(card, 2, "CMDREJi"); - return 1; + return -EIO; } if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { QETH_CARD_TEXT(card, 2, "AFFE"); - return 1; + return -EIO; } if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { QETH_CARD_TEXT(card, 2, "ZEROSEN"); return 0; } QETH_CARD_TEXT(card, 2, "DGENCHK"); - return 1; + return -EIO; } return 0; } @@ -986,8 +988,21 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, QETH_CARD_TEXT(card, 5, "data"); } - if (qeth_intparm_is_iob(intparm)) - iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm); + if (intparm == 0) { + QETH_CARD_TEXT(card, 5, "irqunsol"); + } else if ((addr_t)intparm != (addr_t)channel->active_cmd) { + QETH_CARD_TEXT(card, 5, "irqunexp"); + + dev_err(&cdev->dev, + "Received IRQ with intparm %lx, expected %px\n", + intparm, channel->active_cmd); + if (channel->active_cmd) + qeth_cancel_cmd(channel->active_cmd, -EIO); + } else { + iob = (struct qeth_cmd_buffer *) (addr_t)intparm; + } + + channel->active_cmd = NULL; rc = qeth_check_irb_error(card, cdev, irb); if (rc) { @@ -1007,15 +1022,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) channel->state = CH_STATE_HALTED; - if (intparm == QETH_CLEAR_CHANNEL_PARM) { - QETH_CARD_TEXT(card, 6, "clrchpar"); - /* we don't have to handle this further */ - intparm = 0; - } - if (intparm == QETH_HALT_CHANNEL_PARM) { - QETH_CARD_TEXT(card, 6, "hltchpar"); - /* we don't have to handle this further */ - intparm = 0; + if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC | + SCSW_FCTL_HALT_FUNC))) { + qeth_cancel_cmd(iob, -ECANCELED); + iob = NULL; } cstat = irb->scsw.cmd.cstat; @@ -1408,7 +1418,7 @@ static int qeth_clear_channel(struct qeth_card *card, QETH_CARD_TEXT(card, 3, "clearch"); spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); - rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); + rc = ccw_device_clear(channel->ccwdev, (addr_t)channel->active_cmd); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) @@ -1430,7 +1440,7 @@ static int qeth_halt_channel(struct qeth_card *card, QETH_CARD_TEXT(card, 3, "haltch"); spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); - rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); + rc = ccw_device_halt(channel->ccwdev, (addr_t)channel->active_cmd); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) @@ -1444,6 +1454,25 @@ static int qeth_halt_channel(struct qeth_card *card, return 0; } +int qeth_stop_channel(struct qeth_channel *channel) +{ + struct ccw_device *cdev = channel->ccwdev; + int rc; + + rc = ccw_device_set_offline(cdev); + + spin_lock_irq(get_ccwdev_lock(cdev)); + if (channel->active_cmd) { + dev_err(&cdev->dev, "Stopped channel while cmd %px was still active\n", + channel->active_cmd); + channel->active_cmd = NULL; + } + spin_unlock_irq(get_ccwdev_lock(cdev)); + + return rc; +} +EXPORT_SYMBOL_GPL(qeth_stop_channel); + static int qeth_halt_channels(struct qeth_card *card) { int rc1 = 0, rc2 = 0, rc3 = 0; @@ -1513,7 +1542,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) rc = qeth_clear_halt_card(card, use_halt); if (rc) QETH_CARD_TEXT_(card, 3, "2err%d", rc); - card->state = CARD_STATE_DOWN; return rc; } EXPORT_SYMBOL_GPL(qeth_qdio_clear_card); @@ -1747,6 +1775,8 @@ static int qeth_send_control_data(struct qeth_card *card, spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob), (addr_t) iob, 0, 0, timeout); + if (!rc) + channel->active_cmd = iob; spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) { QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n", @@ -1957,6 +1987,7 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card, ccw_device_get_id(CARD_DDEV(card), &dev_id); iob->finalize = qeth_idx_finalize_cmd; + port |= QETH_IDX_ACT_INVAL_FRAME; memcpy(QETH_IDX_ACT_PNO(iob->data), &port, 1); memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); @@ -2634,6 +2665,18 @@ static int qeth_init_input_buffer(struct qeth_card *card, return 0; } +static unsigned int qeth_tx_select_bulk_max(struct qeth_card *card, + struct qeth_qdio_out_q *queue) +{ + if (!IS_IQD(card) || + qeth_iqd_is_mcast_queue(card, queue) || + card->options.cq == QETH_CQ_ENABLED || + qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd)) + return 1; + + return card->ssqd.mmwc ? card->ssqd.mmwc : 1; +} + int qeth_init_qdio_queues(struct qeth_card *card) { unsigned int i; @@ -2673,6 +2716,8 @@ int qeth_init_qdio_queues(struct qeth_card *card) queue->do_pack = 0; queue->prev_hdr = NULL; queue->bulk_start = 0; + queue->bulk_count = 0; + queue->bulk_max = qeth_tx_select_bulk_max(card, queue); atomic_set(&queue->used_buffers, 0); atomic_set(&queue->set_pci_flags_count, 0); atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); @@ -3080,7 +3125,7 @@ static int qeth_check_qdio_errors(struct qeth_card *card, buf->element[14].sflags); QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); if ((buf->element[15].sflags) == 0x12) { - QETH_CARD_STAT_INC(card, rx_dropped); + QETH_CARD_STAT_INC(card, rx_fifo_errors); return 0; } else return 1; @@ -3107,7 +3152,7 @@ static void qeth_queue_input_buffer(struct qeth_card *card, int index) for (i = queue->next_buf_to_init; i < queue->next_buf_to_init + count; ++i) { if (qeth_init_input_buffer(card, - &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { + &queue->bufs[QDIO_BUFNR(i)])) { break; } else { newcount++; @@ -3149,8 +3194,8 @@ static void qeth_queue_input_buffer(struct qeth_card *card, int index) if (rc) { QETH_CARD_TEXT(card, 2, "qinberr"); } - queue->next_buf_to_init = (queue->next_buf_to_init + count) % - QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_init = QDIO_BUFNR(queue->next_buf_to_init + + count); } } @@ -3198,7 +3243,7 @@ static int qeth_prep_flush_pack_buffer(struct qeth_qdio_out_q *queue) /* it's a packing buffer */ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); queue->next_buf_to_fill = - (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(queue->next_buf_to_fill + 1); return 1; } return 0; @@ -3252,7 +3297,8 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, unsigned int qdio_flags; for (i = index; i < index + count; ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; + unsigned int bidx = QDIO_BUFNR(i); + buf = queue->bufs[bidx]; buf->buffer->element[buf->next_element_to_fill - 1].eflags |= SBAL_EFLAGS_LAST_ENTRY; @@ -3318,10 +3364,11 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, static void qeth_flush_queue(struct qeth_qdio_out_q *queue) { - qeth_flush_buffers(queue, queue->bulk_start, 1); + qeth_flush_buffers(queue, queue->bulk_start, queue->bulk_count); - queue->bulk_start = QDIO_BUFNR(queue->bulk_start + 1); + queue->bulk_start = QDIO_BUFNR(queue->bulk_start + queue->bulk_count); queue->prev_hdr = NULL; + queue->bulk_count = 0; } static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue) @@ -3419,8 +3466,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, } for (i = first_element; i < first_element + count; ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; - struct qdio_buffer *buffer = cq->qdio_bufs[bidx]; + struct qdio_buffer *buffer = cq->qdio_bufs[QDIO_BUFNR(i)]; int e = 0; while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && @@ -3441,8 +3487,8 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, "QDIO reported an error, rc=%i\n", rc); QETH_CARD_TEXT(card, 2, "qcqherr"); } - card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init - + count) % QDIO_MAX_BUFFERS_PER_Q; + + cq->next_buf_to_init = QDIO_BUFNR(cq->next_buf_to_init + count); } static void qeth_qdio_input_handler(struct ccw_device *ccwdev, @@ -3468,7 +3514,6 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, { struct qeth_card *card = (struct qeth_card *) card_ptr; struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; - struct qeth_qdio_out_buffer *buffer; struct net_device *dev = card->dev; struct netdev_queue *txq; int i; @@ -3482,10 +3527,10 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, } for (i = first_element; i < (first_element + count); ++i) { - int bidx = i % QDIO_MAX_BUFFERS_PER_Q; - buffer = queue->bufs[bidx]; - qeth_handle_send_error(card, buffer, qdio_error); - qeth_clear_output_buffer(queue, buffer, qdio_error, 0); + struct qeth_qdio_out_buffer *buf = queue->bufs[QDIO_BUFNR(i)]; + + qeth_handle_send_error(card, buf, qdio_error); + qeth_clear_output_buffer(queue, buf, qdio_error, 0); } atomic_sub(count, &queue->used_buffers); @@ -3680,10 +3725,10 @@ check_layout: } static bool qeth_iqd_may_bulk(struct qeth_qdio_out_q *queue, - struct qeth_qdio_out_buffer *buffer, struct sk_buff *curr_skb, struct qeth_hdr *curr_hdr) { + struct qeth_qdio_out_buffer *buffer = queue->bufs[queue->bulk_start]; struct qeth_hdr *prev_hdr = queue->prev_hdr; if (!prev_hdr) @@ -3803,13 +3848,14 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, struct qeth_hdr *hdr, unsigned int offset, unsigned int hd_len) { - struct qeth_qdio_out_buffer *buffer = queue->bufs[queue->bulk_start]; unsigned int bytes = qdisc_pkt_len(skb); + struct qeth_qdio_out_buffer *buffer; unsigned int next_element; struct netdev_queue *txq; bool stopped = false; bool flush; + buffer = queue->bufs[QDIO_BUFNR(queue->bulk_start + queue->bulk_count)]; txq = netdev_get_tx_queue(card->dev, skb_get_queue_mapping(skb)); /* Just a sanity check, the wake/stop logic should ensure that we always @@ -3818,11 +3864,23 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) return -EBUSY; - if ((buffer->next_element_to_fill + elements > queue->max_elements) || - !qeth_iqd_may_bulk(queue, buffer, skb, hdr)) { - atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - qeth_flush_queue(queue); - buffer = queue->bufs[queue->bulk_start]; + flush = !qeth_iqd_may_bulk(queue, skb, hdr); + + if (flush || + (buffer->next_element_to_fill + elements > queue->max_elements)) { + if (buffer->next_element_to_fill > 0) { + atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); + queue->bulk_count++; + } + + if (queue->bulk_count >= queue->bulk_max) + flush = true; + + if (flush) + qeth_flush_queue(queue); + + buffer = queue->bufs[QDIO_BUFNR(queue->bulk_start + + queue->bulk_count)]; /* Sanity-check again: */ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) @@ -3848,7 +3906,13 @@ static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (flush || next_element >= queue->max_elements) { atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - qeth_flush_queue(queue); + queue->bulk_count++; + + if (queue->bulk_count >= queue->bulk_max) + flush = true; + + if (flush) + qeth_flush_queue(queue); } if (stopped && !qeth_out_queue_is_full(queue)) @@ -3898,8 +3962,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); flush_count++; queue->next_buf_to_fill = - (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(queue->next_buf_to_fill + 1); buffer = queue->bufs[queue->next_buf_to_fill]; /* We stepped forward, so sanity-check again: */ @@ -3932,8 +3995,8 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, if (!queue->do_pack || stopped || next_element >= queue->max_elements) { flush_count++; atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_fill = + QDIO_BUFNR(queue->next_buf_to_fill + 1); } if (flush_count) @@ -4261,7 +4324,6 @@ int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) } return rc; } -EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); void qeth_tx_timeout(struct net_device *dev) { @@ -4316,7 +4378,9 @@ static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) case MII_NWAYTEST: /* N-way auto-neg test register */ break; case MII_RERRCOUNTER: /* rx error counter */ - rc = card->stats.rx_errors; + rc = card->stats.rx_length_errors + + card->stats.rx_frame_errors + + card->stats.rx_fifo_errors; break; case MII_SREVISION: /* silicon revision */ break; @@ -4634,12 +4698,12 @@ EXPORT_SYMBOL_GPL(qeth_vm_request_mac); static void qeth_determine_capabilities(struct qeth_card *card) { + struct qeth_channel *channel = &card->data; + struct ccw_device *ddev = channel->ccwdev; int rc; - struct ccw_device *ddev; int ddev_offline = 0; QETH_CARD_TEXT(card, 2, "detcapab"); - ddev = CARD_DDEV(card); if (!ddev->online) { ddev_offline = 1; rc = ccw_device_set_online(ddev); @@ -4678,7 +4742,7 @@ static void qeth_determine_capabilities(struct qeth_card *card) out_offline: if (ddev_offline == 1) - ccw_device_set_offline(ddev); + qeth_stop_channel(channel); out: return; } @@ -4822,7 +4886,6 @@ static void qeth_core_free_card(struct qeth_card *card) qeth_clean_channel(&card->data); qeth_put_cmd(card->read_cmd); destroy_workqueue(card->event_wq); - qeth_free_qdio_queues(card); unregister_service_level(&card->qeth_service_level); dev_set_drvdata(&card->gdev->dev, NULL); kfree(card); @@ -4879,9 +4942,9 @@ retry: QETH_DBF_MESSAGE(2, "Retrying to do IDX activates on device %x.\n", CARD_DEVID(card)); rc = qeth_qdio_clear_card(card, !IS_IQD(card)); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); rc = ccw_device_set_online(CARD_RDEV(card)); if (rc) @@ -4977,6 +5040,15 @@ retriable: goto out; } } + + if (!qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP) || + (card->info.hwtrap && qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))) + card->info.hwtrap = 0; + + rc = qeth_set_access_ctrl_online(card, 0); + if (rc) + goto out; + return 0; out: dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " @@ -4987,27 +5059,15 @@ out: } EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); -static void qeth_create_skb_frag(struct qdio_buffer_element *element, - struct sk_buff *skb, int offset, int data_len) +static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len) { - struct page *page = virt_to_page(element->addr); + struct page *page = virt_to_page(data); unsigned int next_frag; - /* first fill the linear space */ - if (!skb->len) { - unsigned int linear = min(data_len, skb_tailroom(skb)); - - skb_put_data(skb, element->addr + offset, linear); - data_len -= linear; - if (!data_len) - return; - offset += linear; - /* fall through to add page frag for remaining data */ - } - next_frag = skb_shinfo(skb)->nr_frags; get_page(page); - skb_add_rx_frag(skb, next_frag, page, offset, data_len, data_len); + skb_add_rx_frag(skb, next_frag, page, offset_in_page(data), data_len, + data_len); } static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) @@ -5022,14 +5082,14 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, { struct qdio_buffer_element *element = *__element; struct qdio_buffer *buffer = qethbuffer->buffer; + unsigned int linear_len = 0; int offset = *__offset; + bool use_rx_sg = false; + unsigned int headroom; struct sk_buff *skb; int skb_len = 0; - void *data_ptr; - int data_len; - int headroom = 0; - int use_rx_sg = 0; +next_packet: /* qeth_hdr must not cross element boundaries */ while (element->length < offset + sizeof(struct qeth_hdr)) { if (qeth_is_last_sbale(element)) @@ -5040,71 +5100,125 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, *hdr = element->addr + offset; offset += sizeof(struct qeth_hdr); + skb = NULL; + switch ((*hdr)->hdr.l2.id) { case QETH_HEADER_TYPE_LAYER2: skb_len = (*hdr)->hdr.l2.pkt_length; + linear_len = ETH_HLEN; + headroom = 0; break; case QETH_HEADER_TYPE_LAYER3: skb_len = (*hdr)->hdr.l3.length; + if (!IS_LAYER3(card)) { + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + goto walk_packet; + } + + if ((*hdr)->hdr.l3.flags & QETH_HDR_PASSTHRU) { + linear_len = ETH_HLEN; + headroom = 0; + break; + } + + if ((*hdr)->hdr.l3.flags & QETH_HDR_IPV6) + linear_len = sizeof(struct ipv6hdr); + else + linear_len = sizeof(struct iphdr); headroom = ETH_HLEN; break; case QETH_HEADER_TYPE_OSN: skb_len = (*hdr)->hdr.osn.pdu_length; + if (!IS_OSN(card)) { + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); + goto walk_packet; + } + + linear_len = skb_len; headroom = sizeof(struct qeth_hdr); break; default: - break; - } + if ((*hdr)->hdr.l2.id & QETH_HEADER_MASK_INVAL) + QETH_CARD_STAT_INC(card, rx_frame_errors); + else + QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - if (!skb_len) + /* Can't determine packet length, drop the whole buffer. */ return NULL; + } - if (((skb_len >= card->options.rx_sg_cb) && - !IS_OSN(card) && - (!atomic_read(&card->force_alloc_skb))) || - (card->options.cq == QETH_CQ_ENABLED)) - use_rx_sg = 1; + if (skb_len < linear_len) { + QETH_CARD_STAT_INC(card, rx_dropped_runt); + goto walk_packet; + } + + use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) || + ((skb_len >= card->options.rx_sg_cb) && + !atomic_read(&card->force_alloc_skb) && + !IS_OSN(card)); if (use_rx_sg && qethbuffer->rx_skb) { /* QETH_CQ_ENABLED only: */ skb = qethbuffer->rx_skb; qethbuffer->rx_skb = NULL; } else { - unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len; - - skb = napi_alloc_skb(&card->napi, linear + headroom); + if (!use_rx_sg) + linear_len = skb_len; + skb = napi_alloc_skb(&card->napi, linear_len + headroom); } + if (!skb) - goto no_mem; - if (headroom) + QETH_CARD_STAT_INC(card, rx_dropped_nomem); + else if (headroom) skb_reserve(skb, headroom); - data_ptr = element->addr + offset; +walk_packet: while (skb_len) { - data_len = min(skb_len, (int)(element->length - offset)); - if (data_len) { - if (use_rx_sg) - qeth_create_skb_frag(element, skb, offset, - data_len); - else - skb_put_data(skb, data_ptr, data_len); - } + int data_len = min(skb_len, (int)(element->length - offset)); + char *data = element->addr + offset; + skb_len -= data_len; + offset += data_len; + + /* Extract data from current element: */ + if (skb && data_len) { + if (linear_len) { + unsigned int copy_len; + + copy_len = min_t(unsigned int, linear_len, + data_len); + + skb_put_data(skb, data, copy_len); + linear_len -= copy_len; + data_len -= copy_len; + data += copy_len; + } + + if (data_len) + qeth_create_skb_frag(skb, data, data_len); + } + + /* Step forward to next element: */ if (skb_len) { if (qeth_is_last_sbale(element)) { QETH_CARD_TEXT(card, 4, "unexeob"); QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); - dev_kfree_skb_any(skb); - QETH_CARD_STAT_INC(card, rx_errors); + if (skb) { + dev_kfree_skb_any(skb); + QETH_CARD_STAT_INC(card, + rx_length_errors); + } return NULL; } element++; offset = 0; - data_ptr = element->addr; - } else { - offset += data_len; } } + + /* This packet was skipped, go get another one: */ + if (!skb) + goto next_packet; + *__element = element; *__offset = offset; if (use_rx_sg) { @@ -5113,12 +5227,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, skb_shinfo(skb)->nr_frags); } return skb; -no_mem: - if (net_ratelimit()) { - QETH_CARD_TEXT(card, 2, "noskbmem"); - } - QETH_CARD_STAT_INC(card, rx_dropped); - return NULL; } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); @@ -5165,8 +5273,7 @@ int qeth_poll(struct napi_struct *napi, int budget) card->rx.b_count--; if (card->rx.b_count) { card->rx.b_index = - (card->rx.b_index + 1) % - QDIO_MAX_BUFFERS_PER_Q; + QDIO_BUFNR(card->rx.b_index + 1); card->rx.b_element = &card->qdio.in_q ->bufs[card->rx.b_index] @@ -5182,9 +5289,9 @@ int qeth_poll(struct napi_struct *napi, int budget) } } - napi_complete_done(napi, work_done); - if (qdio_start_irq(card->data.ccwdev, 0)) - napi_schedule(&card->napi); + if (napi_complete_done(napi, work_done) && + qdio_start_irq(CARD_DDEV(card), 0)) + napi_schedule(napi); out: return work_done; } @@ -5703,6 +5810,8 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev) qeth_core_free_discipline(card); } + qeth_free_qdio_queues(card); + free_netdev(card->dev); qeth_core_free_card(card); put_device(&gdev->dev); @@ -6198,9 +6307,16 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->rx_packets = card->stats.rx_packets; stats->rx_bytes = card->stats.rx_bytes; - stats->rx_errors = card->stats.rx_errors; - stats->rx_dropped = card->stats.rx_dropped; + stats->rx_errors = card->stats.rx_length_errors + + card->stats.rx_frame_errors + + card->stats.rx_fifo_errors; + stats->rx_dropped = card->stats.rx_dropped_nomem + + card->stats.rx_dropped_notsupp + + card->stats.rx_dropped_runt; stats->multicast = card->stats.rx_multicast; + stats->rx_length_errors = card->stats.rx_length_errors; + stats->rx_frame_errors = card->stats.rx_frame_errors; + stats->rx_fifo_errors = card->stats.rx_fifo_errors; for (i = 0; i < card->qdio.no_out_queues; i++) { queue = card->qdio.out_qs[i]; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 6420b58cf42b..88f4dc140751 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -11,6 +11,7 @@ #include <asm/qeth.h> #include <uapi/linux/if_ether.h> +#include <uapi/linux/in6.h> #define IPA_PDU_HEADER_SIZE 0x40 #define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer + 0x0e) @@ -28,20 +29,6 @@ extern unsigned char IPA_PDU_HEADER[]; #define QETH_TIMEOUT (10 * HZ) #define QETH_IPA_TIMEOUT (45 * HZ) -#define QETH_CLEAR_CHANNEL_PARM -10 -#define QETH_HALT_CHANNEL_PARM -11 - -static inline bool qeth_intparm_is_iob(unsigned long intparm) -{ - switch (intparm) { - case QETH_CLEAR_CHANNEL_PARM: - case QETH_HALT_CHANNEL_PARM: - case 0: - return false; - } - return true; -} - /*****************************************************************************/ /* IP Assist related definitions */ /*****************************************************************************/ @@ -365,8 +352,7 @@ struct qeth_ipacmd_setdelip6 { struct qeth_ipacmd_setdelipm { __u8 mac[6]; __u8 padding[2]; - __u8 ip6[12]; - __u8 ip4[4]; + struct in6_addr ip; } __attribute__ ((packed)); struct qeth_ipacmd_layer2setdelmac { @@ -900,6 +886,7 @@ extern unsigned char IDX_ACTIVATE_WRITE[]; #define IDX_ACTIVATE_SIZE 0x22 #define QETH_IDX_ACT_PNO(buffer) (buffer+0x0b) #define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer + 0x0c) +#define QETH_IDX_ACT_INVAL_FRAME 0x40 #define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b] & 0x80) #define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer + 0x10) #define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer + 0x16) diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 9f392497d570..e81170ab6d9a 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -20,8 +20,6 @@ static ssize_t qeth_dev_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; switch (card->state) { case CARD_STATE_DOWN: @@ -45,8 +43,6 @@ static ssize_t qeth_dev_chpid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%02X\n", card->info.chpid); } @@ -57,8 +53,7 @@ static ssize_t qeth_dev_if_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; + return sprintf(buf, "%s\n", QETH_CARD_IFNAME(card)); } @@ -68,8 +63,6 @@ static ssize_t qeth_dev_card_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%s\n", qeth_get_cardname_short(card)); } @@ -94,8 +87,6 @@ static ssize_t qeth_dev_inbuf_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%s\n", qeth_get_bufsize_str(card)); } @@ -106,8 +97,6 @@ static ssize_t qeth_dev_portno_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; return sprintf(buf, "%i\n", card->dev->dev_port); } @@ -120,9 +109,6 @@ static ssize_t qeth_dev_portno_store(struct device *dev, unsigned int portno, limit; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -171,9 +157,6 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - switch (card->qdio.do_prio_queueing) { case QETH_PRIO_Q_ING_PREC: return sprintf(buf, "%s\n", "by precedence"); @@ -195,9 +178,6 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); int rc = 0; - if (!card) - return -EINVAL; - if (IS_IQD(card)) return -EOPNOTSUPP; @@ -262,9 +242,6 @@ static ssize_t qeth_dev_bufcnt_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->qdio.in_buf_pool.buf_count); } @@ -276,9 +253,6 @@ static ssize_t qeth_dev_bufcnt_store(struct device *dev, int cnt, old_cnt; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -307,9 +281,6 @@ static ssize_t qeth_dev_recover_store(struct device *dev, char *tmp; int i; - if (!card) - return -EINVAL; - if (!qeth_card_hw_is_reachable(card)) return -EPERM; @@ -325,11 +296,6 @@ static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store); static ssize_t qeth_dev_performance_stats_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct qeth_card *card = dev_get_drvdata(dev); - - if (!card) - return -EINVAL; - return sprintf(buf, "1\n"); } @@ -342,9 +308,6 @@ static ssize_t qeth_dev_performance_stats_store(struct device *dev, bool reset; int rc; - if (!card) - return -EINVAL; - rc = kstrtobool(buf, &reset); if (rc) return rc; @@ -370,9 +333,6 @@ static ssize_t qeth_dev_layer2_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.layer); } @@ -385,9 +345,6 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, int i, rc = 0; enum qeth_discipline_id newdis; - if (!card) - return -EINVAL; - mutex_lock(&card->discipline_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -453,9 +410,6 @@ static ssize_t qeth_dev_isolation_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - switch (card->options.isolation) { case ISOLATION_MODE_NONE: return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE); @@ -475,9 +429,6 @@ static ssize_t qeth_dev_isolation_store(struct device *dev, enum qeth_ipa_isolation_modes isolation; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (!IS_OSD(card) && !IS_OSX(card)) { rc = -EOPNOTSUPP; @@ -522,9 +473,6 @@ static ssize_t qeth_dev_switch_attrs_show(struct device *dev, struct qeth_switch_info sw_info; int rc = 0; - if (!card) - return -EINVAL; - if (!qeth_card_hw_is_reachable(card)) return sprintf(buf, "n/a\n"); @@ -555,8 +503,6 @@ static ssize_t qeth_hw_trap_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; if (card->info.hwtrap) return snprintf(buf, 5, "arm\n"); else @@ -570,9 +516,6 @@ static ssize_t qeth_hw_trap_store(struct device *dev, int rc = 0; int state = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (qeth_card_hw_is_reachable(card)) state = 1; @@ -607,24 +550,12 @@ static ssize_t qeth_hw_trap_store(struct device *dev, static DEVICE_ATTR(hw_trap, 0644, qeth_hw_trap_show, qeth_hw_trap_store); -static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value) -{ - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", value); -} - static ssize_t qeth_dev_blkt_store(struct qeth_card *card, const char *buf, size_t count, int *value, int max_value) { char *tmp; int i, rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -645,7 +576,7 @@ static ssize_t qeth_dev_blkt_total_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, card->info.blkt.time_total); + return sprintf(buf, "%i\n", card->info.blkt.time_total); } static ssize_t qeth_dev_blkt_total_store(struct device *dev, @@ -657,8 +588,6 @@ static ssize_t qeth_dev_blkt_total_store(struct device *dev, &card->info.blkt.time_total, 5000); } - - static DEVICE_ATTR(total, 0644, qeth_dev_blkt_total_show, qeth_dev_blkt_total_store); @@ -667,7 +596,7 @@ static ssize_t qeth_dev_blkt_inter_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, card->info.blkt.inter_packet); + return sprintf(buf, "%i\n", card->info.blkt.inter_packet); } static ssize_t qeth_dev_blkt_inter_store(struct device *dev, @@ -687,8 +616,7 @@ static ssize_t qeth_dev_blkt_inter_jumbo_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - return qeth_dev_blkt_show(buf, card, - card->info.blkt.inter_packet_jumbo); + return sprintf(buf, "%i\n", card->info.blkt.inter_packet_jumbo); } static ssize_t qeth_dev_blkt_inter_jumbo_store(struct device *dev, diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index 096698df3886..ab59bc975719 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -49,6 +49,9 @@ static const struct qeth_stats card_stats[] = { QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs), QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags), QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page), + QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem), + QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp), + QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt), }; #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index bd8143e51747..9086bc04fa6b 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -315,29 +315,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, *done = 1; break; } - switch (hdr->hdr.l2.id) { - case QETH_HEADER_TYPE_LAYER2: + + if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { skb->protocol = eth_type_trans(skb, skb->dev); qeth_rx_csum(card, skb, hdr->hdr.l2.flags[1]); len = skb->len; napi_gro_receive(&card->napi, skb); - break; - case QETH_HEADER_TYPE_OSN: - if (IS_OSN(card)) { - skb_push(skb, sizeof(struct qeth_hdr)); - skb_copy_to_linear_data(skb, hdr, - sizeof(struct qeth_hdr)); - len = skb->len; - card->osn_info.data_cb(skb); - break; - } - /* Else, fall through */ - default: - dev_kfree_skb_any(skb); - QETH_CARD_TEXT(card, 3, "inbunkno"); - QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); - continue; + } else { + skb_push(skb, sizeof(*hdr)); + skb_copy_to_linear_data(skb, hdr, sizeof(*hdr)); + len = skb->len; + card->osn_info.data_cb(skb); } + work_done++; budget--; QETH_CARD_STAT_INC(card, rx_packets); @@ -467,10 +457,14 @@ static void qeth_l2_set_promisc_mode(struct qeth_card *card) if (card->info.promisc_mode == enable) return; - if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) + if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) { qeth_setadp_promisc_mode(card, enable); - else if (card->options.sbp.reflect_promisc) - qeth_l2_promisc_to_bridge(card, enable); + } else { + mutex_lock(&card->sbp_lock); + if (card->options.sbp.reflect_promisc) + qeth_l2_promisc_to_bridge(card, enable); + mutex_unlock(&card->sbp_lock); + } } /* New MAC address is added to the hash table and marked to be written on card @@ -631,6 +625,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) int rc; qeth_l2_vnicc_set_defaults(card); + mutex_init(&card->sbp_lock); if (gdev->dev.type == &qeth_generic_devtype) { rc = qeth_l2_create_device_attributes(&gdev->dev); @@ -759,14 +754,6 @@ add_napi: return rc; } -static int qeth_l2_start_ipassists(struct qeth_card *card) -{ - /* configure isolation level */ - if (qeth_set_access_ctrl_online(card, 0)) - return -ENODEV; - return 0; -} - static void qeth_l2_trace_features(struct qeth_card *card) { /* Set BridgePort features */ @@ -797,17 +784,12 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) goto out_remove; } - if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { - if (card->info.hwtrap && - qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) - card->info.hwtrap = 0; - } else - card->info.hwtrap = 0; - + mutex_lock(&card->sbp_lock); qeth_bridgeport_query_support(card); if (card->options.sbp.supported_funcs) dev_info(&card->gdev->dev, "The device represents a Bridge Capable Port\n"); + mutex_unlock(&card->sbp_lock); qeth_l2_register_dev_addr(card); @@ -825,12 +807,6 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) /* softsetup */ QETH_CARD_TEXT(card, 2, "softsetp"); - if (IS_OSD(card) || IS_OSX(card)) { - rc = qeth_l2_start_ipassists(card); - if (rc) - goto out_remove; - } - rc = qeth_init_qdio_queues(card); if (rc) { QETH_CARD_TEXT_(card, 2, "6err%d", rc); @@ -869,9 +845,9 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) out_remove: qeth_l2_stop_card(card); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); mutex_unlock(&card->conf_mutex); @@ -902,9 +878,9 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, rtnl_unlock(); qeth_l2_stop_card(card); - rc = ccw_device_set_offline(CARD_DDEV(card)); - rc2 = ccw_device_set_offline(CARD_WDEV(card)); - rc3 = ccw_device_set_offline(CARD_RDEV(card)); + rc = qeth_stop_channel(&card->data); + rc2 = qeth_stop_channel(&card->write); + rc3 = qeth_stop_channel(&card->read); if (!rc) rc = (rc2) ? rc2 : rc3; if (rc) @@ -1162,9 +1138,9 @@ static void qeth_bridge_state_change_worker(struct work_struct *work) /* Role should not change by itself, but if it did, */ /* information from the hardware is authoritative. */ - mutex_lock(&data->card->conf_mutex); + mutex_lock(&data->card->sbp_lock); data->card->options.sbp.role = entry->role; - mutex_unlock(&data->card->conf_mutex); + mutex_unlock(&data->card->sbp_lock); snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); snprintf(env_role, sizeof(env_role), "ROLE=%s", @@ -1230,9 +1206,9 @@ static void qeth_bridge_host_event_worker(struct work_struct *work) : (data->hostevs.lost_event_mask == 0x02) ? "Bridge port state change" : "Unknown reason"); - mutex_lock(&data->card->conf_mutex); + mutex_lock(&data->card->sbp_lock); data->card->options.sbp.hostnotification = 0; - mutex_unlock(&data->card->conf_mutex); + mutex_unlock(&data->card->sbp_lock); qeth_bridge_emit_host_event(data->card, anev_abort, 0, NULL, NULL); } else diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index f2c3b127b1e4..f70c7aac2dcc 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -18,12 +18,10 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, int rc = 0; char *word; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); + mutex_lock(&card->sbp_lock); if (qeth_card_hw_is_reachable(card) && card->options.sbp.supported_funcs) rc = qeth_bridgeport_query_ports(card, @@ -57,6 +55,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, else rc = sprintf(buf, "%s\n", word); } + mutex_unlock(&card->sbp_lock); return rc; } @@ -79,8 +78,6 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev, int rc = 0; enum qeth_sbp_roles role; - if (!card) - return -EINVAL; if (sysfs_streq(buf, "primary")) role = QETH_SBP_ROLE_PRIMARY; else if (sysfs_streq(buf, "secondary")) @@ -91,6 +88,7 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev, return -EINVAL; mutex_lock(&card->conf_mutex); + mutex_lock(&card->sbp_lock); if (qeth_l2_vnicc_is_in_use(card)) rc = -EBUSY; @@ -104,6 +102,7 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev, } else card->options.sbp.role = role; + mutex_unlock(&card->sbp_lock); mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -132,9 +131,6 @@ static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); int enabled; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); @@ -150,14 +146,12 @@ static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, bool enable; int rc; - if (!card) - return -EINVAL; - rc = kstrtobool(buf, &enable); if (rc) return rc; mutex_lock(&card->conf_mutex); + mutex_lock(&card->sbp_lock); if (qeth_l2_vnicc_is_in_use(card)) rc = -EBUSY; @@ -168,6 +162,7 @@ static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, } else card->options.sbp.hostnotification = enable; + mutex_unlock(&card->sbp_lock); mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -183,9 +178,6 @@ static ssize_t qeth_bridgeport_reflect_show(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); char *state; - if (!card) - return -EINVAL; - if (qeth_l2_vnicc_is_in_use(card)) return sprintf(buf, "n/a (VNIC characteristics)\n"); @@ -207,9 +199,6 @@ static ssize_t qeth_bridgeport_reflect_store(struct device *dev, int enable, primary; int rc = 0; - if (!card) - return -EINVAL; - if (sysfs_streq(buf, "none")) { enable = 0; primary = 0; @@ -223,6 +212,7 @@ static ssize_t qeth_bridgeport_reflect_store(struct device *dev, return -EINVAL; mutex_lock(&card->conf_mutex); + mutex_lock(&card->sbp_lock); if (qeth_l2_vnicc_is_in_use(card)) rc = -EBUSY; @@ -234,6 +224,7 @@ static ssize_t qeth_bridgeport_reflect_store(struct device *dev, rc = 0; } + mutex_unlock(&card->sbp_lock); mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -269,6 +260,8 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) return; if (!card->options.sbp.supported_funcs) return; + + mutex_lock(&card->sbp_lock); if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { /* Conditional to avoid spurious error messages */ qeth_bridgeport_setrole(card, card->options.sbp.role); @@ -280,8 +273,10 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) rc = qeth_bridgeport_an_set(card, 1); if (rc) card->options.sbp.hostnotification = 0; - } else + } else { qeth_bridgeport_an_set(card, 0); + } + mutex_unlock(&card->sbp_lock); } /* VNIC CHARS support */ @@ -315,9 +310,6 @@ static ssize_t qeth_vnicc_timeout_show(struct device *dev, u32 timeout; int rc; - if (!card) - return -EINVAL; - rc = qeth_l2_vnicc_get_timeout(card, &timeout); if (rc == -EBUSY) return sprintf(buf, "n/a (BridgePort)\n"); @@ -335,9 +327,6 @@ static ssize_t qeth_vnicc_timeout_store(struct device *dev, u32 timeout; int rc; - if (!card) - return -EINVAL; - rc = kstrtou32(buf, 10, &timeout); if (rc) return rc; @@ -357,9 +346,6 @@ static ssize_t qeth_vnicc_char_show(struct device *dev, u32 vnicc; int rc; - if (!card) - return -EINVAL; - vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr->attr.name); rc = qeth_l2_vnicc_get_state(card, vnicc, &state); @@ -380,9 +366,6 @@ static ssize_t qeth_vnicc_char_store(struct device *dev, u32 vnicc; int rc; - if (!card) - return -EINVAL; - if (kstrtobool(buf, &state)) return -EINVAL; diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 87659cfc9066..5db04fe472c0 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -13,8 +13,6 @@ #include "qeth_core.h" #include <linux/hashtable.h> -#define QETH_SNIFF_AVAIL 0x0008 - enum qeth_ip_types { QETH_IP_TYPE_NORMAL, QETH_IP_TYPE_VIPA, @@ -24,7 +22,6 @@ enum qeth_ip_types { struct qeth_ipaddr { struct hlist_node hnode; enum qeth_ip_types type; - unsigned char mac[ETH_ALEN]; u8 is_multicast:1; u8 in_progress:1; u8 disp_flag:2; @@ -37,7 +34,7 @@ struct qeth_ipaddr { enum qeth_prot_versions proto; union { struct { - unsigned int addr; + __be32 addr; unsigned int mask; } a4; struct { @@ -55,6 +52,7 @@ static inline void qeth_l3_init_ipaddr(struct qeth_ipaddr *addr, addr->type = type; addr->proto = proto; addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; + addr->ref_counter = 1; } static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1, @@ -74,12 +72,10 @@ static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1, * so 'proto' and 'addr' match for sure. * * For ucast: - * - 'mac' is always 0. * - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching * values are required to avoid mixups in takeover eligibility. * * For mcast, - * - 'mac' is mapped from the IP, and thus always matches. * - 'mask'/'pfxlen' is always 0. */ if (a1->type != a2->type) @@ -89,21 +85,12 @@ static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1, return a1->u.a4.mask == a2->u.a4.mask; } -static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) +static inline u32 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) { - u64 ret = 0; - u8 *point; - - if (addr->proto == QETH_PROT_IPV6) { - point = (u8 *) &addr->u.a6.addr; - ret = get_unaligned((u64 *)point) ^ - get_unaligned((u64 *) (point + 8)); - } - if (addr->proto == QETH_PROT_IPV4) { - point = (u8 *) &addr->u.a4.addr; - ret = get_unaligned((u32 *) point); - } - return ret; + if (addr->proto == QETH_PROT_IPV6) + return ipv6_addr_hash(&addr->u.a6.addr); + else + return ipv4_addr_hash(addr->u.a4.addr); } struct qeth_ipato_entry { diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index d7bfc7a0e4c0..27126330a4b0 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -39,7 +39,6 @@ static int qeth_l3_set_offline(struct ccwgroup_device *); -static void qeth_l3_set_rx_mode(struct net_device *dev); static int qeth_l3_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *); static int qeth_l3_deregister_addr_entry(struct qeth_card *, @@ -64,19 +63,10 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, qeth_l3_ipaddr6_to_string(addr, buf); } -static struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions prot) -{ - struct qeth_ipaddr *addr = kmalloc(sizeof(*addr), GFP_ATOMIC); - - if (addr) - qeth_l3_init_ipaddr(addr, QETH_IP_TYPE_NORMAL, prot); - return addr; -} - static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, struct qeth_ipaddr *query) { - u64 key = qeth_l3_ipaddr_hash(query); + u32 key = qeth_l3_ipaddr_hash(query); struct qeth_ipaddr *addr; if (query->is_multicast) { @@ -217,13 +207,10 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) "Registering IP address %s failed\n", buf); return -EADDRINUSE; } else { - addr = qeth_l3_get_addr_buffer(tmp_addr->proto); + addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL); if (!addr) return -ENOMEM; - memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr)); - addr->ref_counter = 1; - if (qeth_l3_is_addr_covered_by_ipato(card, addr)) { QETH_CARD_TEXT(card, 2, "tkovaddr"); addr->ipato = 1; @@ -381,12 +368,13 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card, if (!iob) return -ENOMEM; cmd = __ipa_cmd(iob); - ether_addr_copy(cmd->data.setdelipm.mac, addr->mac); - if (addr->proto == QETH_PROT_IPV6) - memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr, - sizeof(struct in6_addr)); - else - memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr, 4); + if (addr->proto == QETH_PROT_IPV6) { + cmd->data.setdelipm.ip = addr->u.a6.addr; + ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac); + } else { + cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr; + ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac); + } return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); } @@ -953,8 +941,6 @@ static int qeth_l3_start_ipassists(struct qeth_card *card) { QETH_CARD_TEXT(card, 3, "strtipas"); - if (qeth_set_access_ctrl_online(card, 0)) - return -EIO; qeth_l3_start_ipa_arp_processing(card); /* go on*/ qeth_l3_start_ipa_source_mac(card); /* go on*/ qeth_l3_start_ipa_vlan(card); /* go on*/ @@ -1115,176 +1101,83 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); } -static void -qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) +static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) { + struct qeth_card *card = arg; + struct inet6_dev *in6_dev; + struct in_device *in4_dev; + struct qeth_ipaddr *ipm; + struct qeth_ipaddr tmp; struct ip_mc_list *im4; - struct qeth_ipaddr *tmp, *ipm; + struct ifmcaddr6 *im6; QETH_CARD_TEXT(card, 4, "addmc"); - tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!tmp) - return; + if (!dev || !(dev->flags & IFF_UP)) + goto out; + + in4_dev = __in_dev_get_rtnl(dev); + if (!in4_dev) + goto walk_ipv6; - for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; - im4 = rcu_dereference(im4->next_rcu)) { - ip_eth_mc_map(im4->multiaddr, tmp->mac); - tmp->u.a4.addr = be32_to_cpu(im4->multiaddr); - tmp->is_multicast = 1; + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; - ipm = qeth_l3_find_addr_by_ip(card, tmp); + for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rtnl_dereference(im4->next_rcu)) { + tmp.u.a4.addr = im4->multiaddr; + + ipm = qeth_l3_find_addr_by_ip(card, &tmp); if (ipm) { /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - } else { - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); - if (!ipm) - continue; - ether_addr_copy(ipm->mac, tmp->mac); - ipm->u.a4.addr = be32_to_cpu(im4->multiaddr); - ipm->is_multicast = 1; - ipm->disp_flag = QETH_DISP_ADDR_ADD; - hash_add(card->ip_mc_htable, - &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); + continue; } - } - - kfree(tmp); -} -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc(struct qeth_card *card) -{ - struct in_device *in_dev; - u16 vid; - - QETH_CARD_TEXT(card, 4, "addmcvl"); - - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), - vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = __in_dev_get_rcu(netdev); - if (!in_dev) + ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL); + if (!ipm) continue; - qeth_l3_add_mc_to_hash(card, in_dev); - } -} - -static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) -{ - struct in_device *in4_dev; - - QETH_CARD_TEXT(card, 4, "chkmcv4"); - rcu_read_lock(); - in4_dev = __in_dev_get_rcu(card->dev); - if (in4_dev == NULL) - goto unlock; - qeth_l3_add_mc_to_hash(card, in4_dev); - qeth_l3_add_vlan_mc(card); -unlock: - rcu_read_unlock(); -} + hash_add(card->ip_mc_htable, &ipm->hnode, + qeth_l3_ipaddr_hash(ipm)); + } -static void qeth_l3_add_mc6_to_hash(struct qeth_card *card, - struct inet6_dev *in6_dev) -{ - struct qeth_ipaddr *ipm; - struct ifmcaddr6 *im6; - struct qeth_ipaddr *tmp; +walk_ipv6: + if (!qeth_is_supported(card, IPA_IPV6)) + goto out; - QETH_CARD_TEXT(card, 4, "addmc6"); + in6_dev = __in6_dev_get(dev); + if (!in6_dev) + goto out; - tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); - if (!tmp) - return; + qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); + tmp.disp_flag = QETH_DISP_ADDR_ADD; + tmp.is_multicast = 1; + read_lock_bh(&in6_dev->lock); for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { - ipv6_eth_mc_map(&im6->mca_addr, tmp->mac); - memcpy(&tmp->u.a6.addr, &im6->mca_addr.s6_addr, - sizeof(struct in6_addr)); - tmp->is_multicast = 1; + tmp.u.a6.addr = im6->mca_addr; - ipm = qeth_l3_find_addr_by_ip(card, tmp); + ipm = qeth_l3_find_addr_by_ip(card, &tmp); if (ipm) { /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; continue; } - ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); + ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC); if (!ipm) continue; - ether_addr_copy(ipm->mac, tmp->mac); - memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr, - sizeof(struct in6_addr)); - ipm->is_multicast = 1; - ipm->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->ip_mc_htable, &ipm->hnode, qeth_l3_ipaddr_hash(ipm)); } - kfree(tmp); -} - -/* called with rcu_read_lock */ -static void qeth_l3_add_vlan_mc6(struct qeth_card *card) -{ - struct inet6_dev *in_dev; - u16 vid; - - QETH_CARD_TEXT(card, 4, "admc6vl"); - - if (!qeth_is_supported(card, IPA_FULL_VLAN)) - return; - - for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { - struct net_device *netdev; - - netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), - vid); - if (netdev == NULL || - !(netdev->flags & IFF_UP)) - continue; - in_dev = in6_dev_get(netdev); - if (!in_dev) - continue; - read_lock_bh(&in_dev->lock); - qeth_l3_add_mc6_to_hash(card, in_dev); - read_unlock_bh(&in_dev->lock); - in6_dev_put(in_dev); - } -} - -static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) -{ - struct inet6_dev *in6_dev; - - QETH_CARD_TEXT(card, 4, "chkmcv6"); - - if (!qeth_is_supported(card, IPA_IPV6)) - return ; - in6_dev = in6_dev_get(card->dev); - if (!in6_dev) - return; - - rcu_read_lock(); - read_lock_bh(&in6_dev->lock); - qeth_l3_add_mc6_to_hash(card, in6_dev); - qeth_l3_add_vlan_mc6(card); read_unlock_bh(&in6_dev->lock); - rcu_read_unlock(); - in6_dev_put(in6_dev); + +out: + return 0; } static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, @@ -1292,7 +1185,7 @@ static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, { struct qeth_card *card = dev->ml_priv; - set_bit(vid, card->active_vlans); + QETH_CARD_TEXT_(card, 4, "aid:%d", vid); return 0; } @@ -1302,9 +1195,6 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, struct qeth_card *card = dev->ml_priv; QETH_CARD_TEXT_(card, 4, "kid:%d", vid); - - clear_bit(vid, card->active_vlans); - qeth_l3_set_rx_mode(dev); return 0; } @@ -1372,7 +1262,6 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, int work_done = 0; struct sk_buff *skb; struct qeth_hdr *hdr; - unsigned int len; *done = 0; WARN_ON_ONCE(!budget); @@ -1384,25 +1273,17 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, *done = 1; break; } - switch (hdr->hdr.l3.id) { - case QETH_HEADER_TYPE_LAYER3: + + if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3) qeth_l3_rebuild_skb(card, skb, hdr); - /* fall through */ - case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */ - skb->protocol = eth_type_trans(skb, skb->dev); - len = skb->len; - napi_gro_receive(&card->napi, skb); - break; - default: - dev_kfree_skb_any(skb); - QETH_CARD_TEXT(card, 3, "inbunkno"); - QETH_DBF_HEX(CTRL, 3, hdr, sizeof(*hdr)); - continue; - } + + skb->protocol = eth_type_trans(skb, skb->dev); + QETH_CARD_STAT_INC(card, rx_packets); + QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); + + napi_gro_receive(&card->napi, skb); work_done++; budget--; - QETH_CARD_STAT_INC(card, rx_packets); - QETH_CARD_STAT_ADD(card, rx_bytes, len); } return work_done; } @@ -1468,8 +1349,11 @@ static void qeth_l3_rx_mode_work(struct work_struct *work) QETH_CARD_TEXT(card, 3, "setmulti"); if (!card->options.sniffer) { - qeth_l3_add_multicast_ipv4(card); - qeth_l3_add_multicast_ipv6(card); + rtnl_lock(); + qeth_l3_add_mcast_rtnl(card->dev, 0, card); + if (qeth_is_supported(card, IPA_FULL_VLAN)) + vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card); + rtnl_unlock(); hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) { switch (addr->disp_flag) { @@ -2313,13 +2197,6 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev) goto out_remove; } - if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { - if (card->info.hwtrap && - qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) - card->info.hwtrap = 0; - } else - card->info.hwtrap = 0; - card->state = CARD_STATE_HARDSETUP; qeth_print_status_message(card); @@ -2382,9 +2259,9 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev) return 0; out_remove: qeth_l3_stop_card(card); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); mutex_unlock(&card->conf_mutex); @@ -2420,9 +2297,10 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, call_netdevice_notifiers(NETDEV_REBOOT, card->dev); rtnl_unlock(); } - rc = ccw_device_set_offline(CARD_DDEV(card)); - rc2 = ccw_device_set_offline(CARD_WDEV(card)); - rc3 = ccw_device_set_offline(CARD_RDEV(card)); + + rc = qeth_stop_channel(&card->data); + rc2 = qeth_stop_channel(&card->write); + rc3 = qeth_stop_channel(&card->read); if (!rc) rc = (rc2) ? rc2 : rc3; if (rc) @@ -2557,7 +2435,7 @@ static int qeth_l3_ip_event(struct notifier_block *this, QETH_CARD_TEXT(card, 3, "ipevent"); qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); - addr.u.a4.addr = be32_to_cpu(ifa->ifa_address); + addr.u.a4.addr = ifa->ifa_address; addr.u.a4.mask = be32_to_cpu(ifa->ifa_mask); return qeth_l3_handle_ip_event(card, &addr, event); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 2f73b33c9347..f9067ed6c7d3 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -60,9 +60,6 @@ static ssize_t qeth_l3_dev_route4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_show(card, &card->options.route4, buf); } @@ -109,9 +106,6 @@ static ssize_t qeth_l3_dev_route4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_store(card, &card->options.route4, QETH_PROT_IPV4, buf, count); } @@ -124,9 +118,6 @@ static ssize_t qeth_l3_dev_route6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_show(card, &card->options.route6, buf); } @@ -135,9 +126,6 @@ static ssize_t qeth_l3_dev_route6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_route_store(card, &card->options.route6, QETH_PROT_IPV6, buf, count); } @@ -150,9 +138,6 @@ static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); } @@ -163,9 +148,6 @@ static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev, char *tmp; int i, rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -190,9 +172,6 @@ static ssize_t qeth_l3_dev_sniffer_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0); } @@ -203,9 +182,6 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev, int rc = 0; unsigned long i; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; if (card->options.cq == QETH_CQ_ENABLED) @@ -228,7 +204,7 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev, break; case 1: qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd); - if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) { + if (card->ssqd.qdioac2 & CHSC_AC2_SNIFFER_AVAILABLE) { card->options.sniffer = i; if (card->qdio.init_pool.buf_count != QETH_IN_BUF_COUNT_MAX) @@ -248,16 +224,12 @@ out: static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show, qeth_l3_dev_sniffer_store); - static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); char tmp_hsuid[9]; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; @@ -273,9 +245,6 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, char *tmp; int rc; - if (!card) - return -EINVAL; - if (!IS_IQD(card)) return -EPERM; if (card->state != CARD_STATE_DOWN) @@ -336,9 +305,6 @@ static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); } @@ -349,9 +315,6 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, bool enable; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (card->state != CARD_STATE_DOWN) { rc = -EPERM; @@ -385,9 +348,6 @@ static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); } @@ -399,9 +359,6 @@ static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev, bool invert; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (sysfs_streq(buf, "toggle")) { invert = !card->ipato.invert4; @@ -460,9 +417,6 @@ static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); } @@ -528,9 +482,6 @@ static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -558,9 +509,6 @@ static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -572,9 +520,6 @@ static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); } @@ -585,9 +530,6 @@ static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev, bool invert; int rc = 0; - if (!card) - return -EINVAL; - mutex_lock(&card->conf_mutex); if (sysfs_streq(buf, "toggle")) { invert = !card->ipato.invert6; @@ -617,9 +559,6 @@ static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); } @@ -628,9 +567,6 @@ static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -643,9 +579,6 @@ static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); } @@ -679,9 +612,6 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf, int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int i; - if (!card) - return -EINVAL; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len += 2; /* \n + terminator */ mutex_lock(&card->ip_lock); @@ -741,9 +671,6 @@ static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -771,9 +698,6 @@ static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -793,9 +717,6 @@ static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -808,9 +729,6 @@ static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); } @@ -884,9 +802,6 @@ static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); } @@ -914,9 +829,6 @@ static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); } @@ -936,9 +848,6 @@ static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); } @@ -951,9 +860,6 @@ static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); - if (!card) - return -EINVAL; - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); } diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile index 9dda431ec8f3..352056eb0dd1 100644 --- a/drivers/s390/scsi/Makefile +++ b/drivers/s390/scsi/Makefile @@ -5,6 +5,6 @@ zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_dbf.o zfcp_erp.o \ zfcp_fc.o zfcp_fsf.o zfcp_qdio.o zfcp_scsi.o zfcp_sysfs.o \ - zfcp_unit.o + zfcp_unit.o zfcp_diag.o obj-$(CONFIG_ZFCP) += zfcp.o diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index e390f8c6d5f3..09ec846fe01d 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -4,7 +4,7 @@ * * Module interface and handling of zfcp data structures. * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2018 */ /* @@ -25,6 +25,7 @@ * Martin Petermann * Sven Schuetz * Steffen Maier + * Benjamin Block */ #define KMSG_COMPONENT "zfcp" @@ -36,6 +37,7 @@ #include "zfcp_ext.h" #include "zfcp_fc.h" #include "zfcp_reqlist.h" +#include "zfcp_diag.h" #define ZFCP_BUS_ID_SIZE 20 @@ -356,6 +358,9 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->erp_action.adapter = adapter; + if (zfcp_diag_adapter_setup(adapter)) + goto failed; + if (zfcp_qdio_setup(adapter)) goto failed; @@ -402,6 +407,9 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) &zfcp_sysfs_adapter_attrs)) goto failed; + if (zfcp_diag_sysfs_setup(adapter)) + goto failed; + /* report size limit per scatter-gather segment */ adapter->ccw_device->dev.dma_parms = &adapter->dma_parms; @@ -426,6 +434,7 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter) zfcp_fc_wka_ports_force_offline(adapter->gs); zfcp_scsi_adapter_unregister(adapter); + zfcp_diag_sysfs_destroy(adapter); sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); zfcp_erp_thread_kill(adapter); @@ -449,6 +458,7 @@ void zfcp_adapter_release(struct kref *ref) dev_set_drvdata(&adapter->ccw_device->dev, NULL); zfcp_fc_gs_destroy(adapter); zfcp_free_low_mem_buffers(adapter); + zfcp_diag_adapter_free(adapter); kfree(adapter->req_list); kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index dccdb41bed8c..1234294700c4 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -95,11 +95,9 @@ void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req) memcpy(rec->u.res.fsf_status_qual, &q_head->fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE); - if (q_head->fsf_command != FSF_QTCB_FCP_CMND) { - rec->pl_len = q_head->log_length; - zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start, - rec->pl_len, "fsf_res", req->req_id); - } + rec->pl_len = q_head->log_length; + zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start, + rec->pl_len, "fsf_res", req->req_id); debug_event(dbf->hba, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 87d2f47a6990..8cc0eefe4ccc 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -4,7 +4,7 @@ * * Global definitions for the zfcp device driver. * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2018 */ #ifndef ZFCP_DEF_H @@ -86,6 +86,7 @@ #define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED 0x00000080 #define ZFCP_STATUS_FSFREQ_TMFUNCFAILED 0x00000200 #define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000 +#define ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE 0x00020000 /************************* STRUCTURE DEFINITIONS *****************************/ @@ -197,6 +198,7 @@ struct zfcp_adapter { struct device_dma_parameters dma_parms; struct zfcp_fc_events events; unsigned long next_port_scan; + struct zfcp_diag_adapter *diagnostics; }; struct zfcp_port { diff --git a/drivers/s390/scsi/zfcp_diag.c b/drivers/s390/scsi/zfcp_diag.c new file mode 100644 index 000000000000..67a8f4e57db1 --- /dev/null +++ b/drivers/s390/scsi/zfcp_diag.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * zfcp device driver + * + * Functions to handle diagnostics. + * + * Copyright IBM Corp. 2018 + */ + +#include <linux/spinlock.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/kernfs.h> +#include <linux/sysfs.h> +#include <linux/errno.h> +#include <linux/slab.h> + +#include "zfcp_diag.h" +#include "zfcp_ext.h" +#include "zfcp_def.h" + +static DECLARE_WAIT_QUEUE_HEAD(__zfcp_diag_publish_wait); + +/** + * zfcp_diag_adapter_setup() - Setup storage for adapter diagnostics. + * @adapter: the adapter to setup diagnostics for. + * + * Creates the data-structures to store the diagnostics for an adapter. This + * overwrites whatever was stored before at &zfcp_adapter->diagnostics! + * + * Return: + * * 0 - Everyting is OK + * * -ENOMEM - Could not allocate all/parts of the data-structures; + * &zfcp_adapter->diagnostics remains unchanged + */ +int zfcp_diag_adapter_setup(struct zfcp_adapter *const adapter) +{ + struct zfcp_diag_adapter *diag; + struct zfcp_diag_header *hdr; + + diag = kzalloc(sizeof(*diag), GFP_KERNEL); + if (diag == NULL) + return -ENOMEM; + + diag->max_age = (5 * 1000); /* default value: 5 s */ + + /* setup header for port_data */ + hdr = &diag->port_data.header; + + spin_lock_init(&hdr->access_lock); + hdr->buffer = &diag->port_data.data; + hdr->buffer_size = sizeof(diag->port_data.data); + /* set the timestamp so that the first test on age will always fail */ + hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age); + + /* setup header for config_data */ + hdr = &diag->config_data.header; + + spin_lock_init(&hdr->access_lock); + hdr->buffer = &diag->config_data.data; + hdr->buffer_size = sizeof(diag->config_data.data); + /* set the timestamp so that the first test on age will always fail */ + hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age); + + adapter->diagnostics = diag; + return 0; +} + +/** + * zfcp_diag_adapter_free() - Frees all adapter diagnostics allocations. + * @adapter: the adapter whose diagnostic structures should be freed. + * + * Frees all data-structures in the given adapter that store diagnostics + * information. Can savely be called with partially setup diagnostics. + */ +void zfcp_diag_adapter_free(struct zfcp_adapter *const adapter) +{ + kfree(adapter->diagnostics); + adapter->diagnostics = NULL; +} + +/** + * zfcp_diag_sysfs_setup() - Setup the sysfs-group for adapter-diagnostics. + * @adapter: target adapter to which the group should be added. + * + * Return: 0 on success; Something else otherwise (see sysfs_create_group()). + */ +int zfcp_diag_sysfs_setup(struct zfcp_adapter *const adapter) +{ + int rc = sysfs_create_group(&adapter->ccw_device->dev.kobj, + &zfcp_sysfs_diag_attr_group); + if (rc == 0) + adapter->diagnostics->sysfs_established = 1; + + return rc; +} + +/** + * zfcp_diag_sysfs_destroy() - Remove the sysfs-group for adapter-diagnostics. + * @adapter: target adapter from which the group should be removed. + */ +void zfcp_diag_sysfs_destroy(struct zfcp_adapter *const adapter) +{ + if (adapter->diagnostics == NULL || + !adapter->diagnostics->sysfs_established) + return; + + /* + * We need this state-handling so we can prevent warnings being printed + * on the kernel-console in case we have to abort a halfway done + * zfcp_adapter_enqueue(), in which the sysfs-group was not yet + * established. sysfs_remove_group() does this checking as well, but + * still prints a warning in case we try to remove a group that has not + * been established before + */ + adapter->diagnostics->sysfs_established = 0; + sysfs_remove_group(&adapter->ccw_device->dev.kobj, + &zfcp_sysfs_diag_attr_group); +} + + +/** + * zfcp_diag_update_xdata() - Update a diagnostics buffer. + * @hdr: the meta data to update. + * @data: data to use for the update. + * @incomplete: flag stating whether the data in @data is incomplete. + */ +void zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr, + const void *const data, const bool incomplete) +{ + const unsigned long capture_timestamp = jiffies; + unsigned long flags; + + spin_lock_irqsave(&hdr->access_lock, flags); + + /* make sure we never go into the past with an update */ + if (!time_after_eq(capture_timestamp, hdr->timestamp)) + goto out; + + hdr->timestamp = capture_timestamp; + hdr->incomplete = incomplete; + memcpy(hdr->buffer, data, hdr->buffer_size); +out: + spin_unlock_irqrestore(&hdr->access_lock, flags); +} + +/** + * zfcp_diag_update_port_data_buffer() - Implementation of + * &typedef zfcp_diag_update_buffer_func + * to collect and update Port Data. + * @adapter: Adapter to collect Port Data from. + * + * This call is SYNCHRONOUS ! It blocks till the respective command has + * finished completely, or has failed in some way. + * + * Return: + * * 0 - Successfully retrieved new Diagnostics and Updated the buffer; + * this also includes cases where data was retrieved, but + * incomplete; you'll have to check the flag ``incomplete`` + * of &struct zfcp_diag_header. + * * see zfcp_fsf_exchange_port_data_sync() for possible error-codes ( + * excluding -EAGAIN) + */ +int zfcp_diag_update_port_data_buffer(struct zfcp_adapter *const adapter) +{ + int rc; + + rc = zfcp_fsf_exchange_port_data_sync(adapter->qdio, NULL); + if (rc == -EAGAIN) + rc = 0; /* signaling incomplete via struct zfcp_diag_header */ + + /* buffer-data was updated in zfcp_fsf_exchange_port_data_handler() */ + + return rc; +} + +/** + * zfcp_diag_update_config_data_buffer() - Implementation of + * &typedef zfcp_diag_update_buffer_func + * to collect and update Config Data. + * @adapter: Adapter to collect Config Data from. + * + * This call is SYNCHRONOUS ! It blocks till the respective command has + * finished completely, or has failed in some way. + * + * Return: + * * 0 - Successfully retrieved new Diagnostics and Updated the buffer; + * this also includes cases where data was retrieved, but + * incomplete; you'll have to check the flag ``incomplete`` + * of &struct zfcp_diag_header. + * * see zfcp_fsf_exchange_config_data_sync() for possible error-codes ( + * excluding -EAGAIN) + */ +int zfcp_diag_update_config_data_buffer(struct zfcp_adapter *const adapter) +{ + int rc; + + rc = zfcp_fsf_exchange_config_data_sync(adapter->qdio, NULL); + if (rc == -EAGAIN) + rc = 0; /* signaling incomplete via struct zfcp_diag_header */ + + /* buffer-data was updated in zfcp_fsf_exchange_config_data_handler() */ + + return rc; +} + +static int __zfcp_diag_update_buffer(struct zfcp_adapter *const adapter, + struct zfcp_diag_header *const hdr, + zfcp_diag_update_buffer_func buffer_update, + unsigned long *const flags) + __must_hold(hdr->access_lock) +{ + int rc; + + if (hdr->updating == 1) { + rc = wait_event_interruptible_lock_irq(__zfcp_diag_publish_wait, + hdr->updating == 0, + hdr->access_lock); + rc = (rc == 0 ? -EAGAIN : -EINTR); + } else { + hdr->updating = 1; + spin_unlock_irqrestore(&hdr->access_lock, *flags); + + /* unlocked, because update function sleeps */ + rc = buffer_update(adapter); + + spin_lock_irqsave(&hdr->access_lock, *flags); + hdr->updating = 0; + + /* + * every thread waiting here went via an interruptible wait, + * so its fine to only wake those + */ + wake_up_interruptible_all(&__zfcp_diag_publish_wait); + } + + return rc; +} + +static bool +__zfcp_diag_test_buffer_age_isfresh(const struct zfcp_diag_adapter *const diag, + const struct zfcp_diag_header *const hdr) + __must_hold(hdr->access_lock) +{ + const unsigned long now = jiffies; + + /* + * Should not happen (data is from the future).. if it does, still + * signal that it needs refresh + */ + if (!time_after_eq(now, hdr->timestamp)) + return false; + + if (jiffies_to_msecs(now - hdr->timestamp) >= diag->max_age) + return false; + + return true; +} + +/** + * zfcp_diag_update_buffer_limited() - Collect diagnostics and update a + * diagnostics buffer rate limited. + * @adapter: Adapter to collect the diagnostics from. + * @hdr: buffer-header for which to update with the collected diagnostics. + * @buffer_update: Specific implementation for collecting and updating. + * + * This function will cause an update of the given @hdr by calling the also + * given @buffer_update function. If called by multiple sources at the same + * time, it will synchornize the update by only allowing one source to call + * @buffer_update and the others to wait for that source to complete instead + * (the wait is interruptible). + * + * Additionally this version is rate-limited and will only exit if either the + * buffer is fresh enough (within the limit) - it will do nothing if the buffer + * is fresh enough to begin with -, or if the source/thread that started this + * update is the one that made the update (to prevent endless loops). + * + * Return: + * * 0 - If the update was successfully published and/or the buffer is + * fresh enough + * * -EINTR - If the thread went into the wait-state and was interrupted + * * whatever @buffer_update returns + */ +int zfcp_diag_update_buffer_limited(struct zfcp_adapter *const adapter, + struct zfcp_diag_header *const hdr, + zfcp_diag_update_buffer_func buffer_update) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&hdr->access_lock, flags); + + for (rc = 0; + !__zfcp_diag_test_buffer_age_isfresh(adapter->diagnostics, hdr); + rc = 0) { + rc = __zfcp_diag_update_buffer(adapter, hdr, buffer_update, + &flags); + if (rc != -EAGAIN) + break; + } + + spin_unlock_irqrestore(&hdr->access_lock, flags); + + return rc; +} diff --git a/drivers/s390/scsi/zfcp_diag.h b/drivers/s390/scsi/zfcp_diag.h new file mode 100644 index 000000000000..b9c93d15f67c --- /dev/null +++ b/drivers/s390/scsi/zfcp_diag.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * zfcp device driver + * + * Definitions for handling diagnostics in the the zfcp device driver. + * + * Copyright IBM Corp. 2018 + */ + +#ifndef ZFCP_DIAG_H +#define ZFCP_DIAG_H + +#include <linux/spinlock.h> + +#include "zfcp_fsf.h" +#include "zfcp_def.h" + +/** + * struct zfcp_diag_header - general part of a diagnostic buffer. + * @access_lock: lock protecting all the data in this buffer. + * @updating: flag showing that an update for this buffer is currently running. + * @incomplete: flag showing that the data in @buffer is incomplete. + * @timestamp: time in jiffies when the data of this buffer was last captured. + * @buffer: implementation-depending data of this buffer + * @buffer_size: size of @buffer + */ +struct zfcp_diag_header { + spinlock_t access_lock; + + /* Flags */ + u64 updating :1; + u64 incomplete :1; + + unsigned long timestamp; + + void *buffer; + size_t buffer_size; +}; + +/** + * struct zfcp_diag_adapter - central storage for all diagnostics concerning an + * adapter. + * @sysfs_established: flag showing that the associated sysfs-group was created + * during run of zfcp_adapter_enqueue(). + * @max_age: maximum age of data in diagnostic buffers before they need to be + * refreshed (in ms). + * @port_data: data retrieved using exchange port data. + * @port_data.header: header with metadata for the cache in @port_data.data. + * @port_data.data: cached QTCB Bottom of command exchange port data. + * @config_data: data retrieved using exchange config data. + * @config_data.header: header with metadata for the cache in @config_data.data. + * @config_data.data: cached QTCB Bottom of command exchange config data. + */ +struct zfcp_diag_adapter { + u64 sysfs_established :1; + + unsigned long max_age; + + struct { + struct zfcp_diag_header header; + struct fsf_qtcb_bottom_port data; + } port_data; + struct { + struct zfcp_diag_header header; + struct fsf_qtcb_bottom_config data; + } config_data; +}; + +int zfcp_diag_adapter_setup(struct zfcp_adapter *const adapter); +void zfcp_diag_adapter_free(struct zfcp_adapter *const adapter); + +int zfcp_diag_sysfs_setup(struct zfcp_adapter *const adapter); +void zfcp_diag_sysfs_destroy(struct zfcp_adapter *const adapter); + +void zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr, + const void *const data, const bool incomplete); + +/* + * Function-Type used in zfcp_diag_update_buffer_limited() for the function + * that does the buffer-implementation dependent work. + */ +typedef int (*zfcp_diag_update_buffer_func)(struct zfcp_adapter *const adapter); + +int zfcp_diag_update_config_data_buffer(struct zfcp_adapter *const adapter); +int zfcp_diag_update_port_data_buffer(struct zfcp_adapter *const adapter); +int zfcp_diag_update_buffer_limited(struct zfcp_adapter *const adapter, + struct zfcp_diag_header *const hdr, + zfcp_diag_update_buffer_func buffer_update); + +/** + * zfcp_diag_support_sfp() - Return %true if the @adapter supports reporting + * SFP Data. + * @adapter: adapter to test the availability of SFP Data reporting for. + */ +static inline bool +zfcp_diag_support_sfp(const struct zfcp_adapter *const adapter) +{ + return !!(adapter->adapter_features & FSF_FEATURE_REPORT_SFP_DATA); +} + +#endif /* ZFCP_DIAG_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 96f0d34e9459..93655b85b73f 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -174,7 +174,7 @@ static enum zfcp_erp_act_type zfcp_erp_required_act(enum zfcp_erp_act_type want, return 0; p_status = atomic_read(&port->status); if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) || - p_status & ZFCP_STATUS_COMMON_ERP_FAILED) + p_status & ZFCP_STATUS_COMMON_ERP_FAILED) return 0; if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED)) need = ZFCP_ERP_ACTION_REOPEN_PORT; @@ -190,7 +190,7 @@ static enum zfcp_erp_act_type zfcp_erp_required_act(enum zfcp_erp_act_type want, return 0; a_status = atomic_read(&adapter->status); if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) || - a_status & ZFCP_STATUS_COMMON_ERP_FAILED) + a_status & ZFCP_STATUS_COMMON_ERP_FAILED) return 0; if (p_status & ZFCP_STATUS_COMMON_NOESC) return need; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 31e8a7240fd7..c8556787cfdc 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -167,6 +167,7 @@ extern const struct attribute_group *zfcp_port_attr_groups[]; extern struct mutex zfcp_sysfs_port_units_mutex; extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; extern struct device_attribute *zfcp_sysfs_shost_attrs[]; +extern const struct attribute_group zfcp_sysfs_diag_attr_group; bool zfcp_sysfs_port_is_removing(const struct zfcp_port *const port); /* zfcp_unit.c */ diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index cf63916814cc..223a805f0b0b 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/blktrace_api.h> +#include <linux/jiffies.h> #include <linux/types.h> #include <linux/slab.h> #include <scsi/fc/fc_els.h> @@ -19,6 +20,7 @@ #include "zfcp_dbf.h" #include "zfcp_qdio.h" #include "zfcp_reqlist.h" +#include "zfcp_diag.h" /* timeout for FSF requests sent during scsi_eh: abort or FCP TMF */ #define ZFCP_FSF_SCSI_ER_TIMEOUT (10*HZ) @@ -554,6 +556,8 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; + struct zfcp_diag_header *const diag_hdr = + &adapter->diagnostics->config_data.header; struct fsf_qtcb *qtcb = req->qtcb; struct fsf_qtcb_bottom_config *bottom = &qtcb->bottom.config; struct Scsi_Host *shost = adapter->scsi_host; @@ -570,6 +574,12 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) switch (qtcb->header.fsf_status) { case FSF_GOOD: + /* + * usually we wait with an update till the cache is too old, + * but because we have the data available, update it anyway + */ + zfcp_diag_update_xdata(diag_hdr, bottom, false); + if (zfcp_fsf_exchange_config_evaluate(req)) return; @@ -585,6 +595,9 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) &adapter->status); break; case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: + zfcp_diag_update_xdata(diag_hdr, bottom, true); + req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE; + fc_host_node_name(shost) = 0; fc_host_port_name(shost) = 0; fc_host_port_id(shost) = 0; @@ -653,16 +666,28 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req) static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) { + struct zfcp_diag_header *const diag_hdr = + &req->adapter->diagnostics->port_data.header; struct fsf_qtcb *qtcb = req->qtcb; + struct fsf_qtcb_bottom_port *bottom = &qtcb->bottom.port; if (req->status & ZFCP_STATUS_FSFREQ_ERROR) return; switch (qtcb->header.fsf_status) { case FSF_GOOD: + /* + * usually we wait with an update till the cache is too old, + * but because we have the data available, update it anyway + */ + zfcp_diag_update_xdata(diag_hdr, bottom, false); + zfcp_fsf_exchange_port_evaluate(req); break; case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: + zfcp_diag_update_xdata(diag_hdr, bottom, true); + req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE; + zfcp_fsf_exchange_port_evaluate(req); zfcp_fsf_link_down_info_eval(req, &qtcb->header.fsf_status_qual.link_down_info); @@ -1261,7 +1286,8 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) req->qtcb->bottom.config.feature_selection = FSF_FEATURE_NOTIFICATION_LOST | - FSF_FEATURE_UPDATE_ALERT; + FSF_FEATURE_UPDATE_ALERT | + FSF_FEATURE_REQUEST_SFP_DATA; req->erp_action = erp_action; req->handler = zfcp_fsf_exchange_config_data_handler; erp_action->fsf_req_id = req->req_id; @@ -1278,6 +1304,19 @@ out: return retval; } + +/** + * zfcp_fsf_exchange_config_data_sync() - Request information about FCP channel. + * @qdio: pointer to the QDIO-Queue to use for sending the command. + * @data: pointer to the QTCB-Bottom for storing the result of the command, + * might be %NULL. + * + * Returns: + * * 0 - Exchange Config Data was successful, @data is complete + * * -EIO - Exchange Config Data was not successful, @data is invalid + * * -EAGAIN - @data contains incomplete data + * * -ENOMEM - Some memory allocation failed along the way + */ int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio, struct fsf_qtcb_bottom_config *data) { @@ -1301,7 +1340,8 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio, req->qtcb->bottom.config.feature_selection = FSF_FEATURE_NOTIFICATION_LOST | - FSF_FEATURE_UPDATE_ALERT; + FSF_FEATURE_UPDATE_ALERT | + FSF_FEATURE_REQUEST_SFP_DATA; if (data) req->data = data; @@ -1309,9 +1349,16 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio, zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); retval = zfcp_fsf_req_send(req); spin_unlock_irq(&qdio->req_q_lock); + if (!retval) { /* NOTE: ONLY TOUCH SYNC req AGAIN ON req->completion. */ wait_for_completion(&req->completion); + + if (req->status & + (ZFCP_STATUS_FSFREQ_ERROR | ZFCP_STATUS_FSFREQ_DISMISSED)) + retval = -EIO; + else if (req->status & ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE) + retval = -EAGAIN; } zfcp_fsf_req_free(req); @@ -1369,10 +1416,17 @@ out: } /** - * zfcp_fsf_exchange_port_data_sync - request information about local port - * @qdio: pointer to struct zfcp_qdio - * @data: pointer to struct fsf_qtcb_bottom_port - * Returns: 0 on success, error otherwise + * zfcp_fsf_exchange_port_data_sync() - Request information about local port. + * @qdio: pointer to the QDIO-Queue to use for sending the command. + * @data: pointer to the QTCB-Bottom for storing the result of the command, + * might be %NULL. + * + * Returns: + * * 0 - Exchange Port Data was successful, @data is complete + * * -EIO - Exchange Port Data was not successful, @data is invalid + * * -EAGAIN - @data contains incomplete data + * * -ENOMEM - Some memory allocation failed along the way + * * -EOPNOTSUPP - This operation is not supported */ int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, struct fsf_qtcb_bottom_port *data) @@ -1408,10 +1462,15 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, if (!retval) { /* NOTE: ONLY TOUCH SYNC req AGAIN ON req->completion. */ wait_for_completion(&req->completion); + + if (req->status & + (ZFCP_STATUS_FSFREQ_ERROR | ZFCP_STATUS_FSFREQ_DISMISSED)) + retval = -EIO; + else if (req->status & ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE) + retval = -EAGAIN; } zfcp_fsf_req_free(req); - return retval; out_unlock: diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 2c658b66318c..2b1e4da1944f 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -163,6 +163,8 @@ #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 #define FSF_FEATURE_UPDATE_ALERT 0x00000100 #define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 +#define FSF_FEATURE_REQUEST_SFP_DATA 0x00000200 +#define FSF_FEATURE_REPORT_SFP_DATA 0x00000800 #define FSF_FEATURE_DIF_PROT_TYPE1 0x00010000 #define FSF_FEATURE_DIX_PROT_TCPIP 0x00020000 @@ -407,7 +409,24 @@ struct fsf_qtcb_bottom_port { u8 cp_util; u8 cb_util; u8 a_util; - u8 res2[253]; + u8 res2; + u16 temperature; + u16 vcc; + u16 tx_bias; + u16 tx_power; + u16 rx_power; + union { + u16 raw; + struct { + u16 fec_active :1; + u16:7; + u16 connector_type :2; + u16 sfp_invalid :1; + u16 optical_port :1; + u16 port_tx_type :4; + }; + } sfp_flags; + u8 res3[240]; } __attribute__ ((packed)); union fsf_qtcb_bottom { diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e9ded2befa0d..3910d529c15a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -605,7 +605,7 @@ zfcp_scsi_get_fc_host_stats(struct Scsi_Host *host) return NULL; ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); - if (ret) { + if (ret != 0 && ret != -EAGAIN) { kfree(data); return NULL; } @@ -634,7 +634,7 @@ static void zfcp_scsi_reset_fc_host_stats(struct Scsi_Host *shost) return; ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); - if (ret) + if (ret != 0 && ret != -EAGAIN) kfree(data); else { adapter->stats_reset = jiffies/HZ; diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index af197e2b3e69..494b9fe9cc94 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/slab.h> +#include "zfcp_diag.h" #include "zfcp_ext.h" #define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \ @@ -325,6 +326,50 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store); +static ssize_t +zfcp_sysfs_adapter_diag_max_age_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); + ssize_t rc; + + if (!adapter) + return -ENODEV; + + /* ceil(log(2^64 - 1) / log(10)) = 20 */ + rc = scnprintf(buf, 20 + 2, "%lu\n", adapter->diagnostics->max_age); + + zfcp_ccw_adapter_put(adapter); + return rc; +} + +static ssize_t +zfcp_sysfs_adapter_diag_max_age_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); + unsigned long max_age; + ssize_t rc; + + if (!adapter) + return -ENODEV; + + rc = kstrtoul(buf, 10, &max_age); + if (rc != 0) + goto out; + + adapter->diagnostics->max_age = max_age; + + rc = count; +out: + zfcp_ccw_adapter_put(adapter); + return rc; +} +static ZFCP_DEV_ATTR(adapter, diag_max_age, 0644, + zfcp_sysfs_adapter_diag_max_age_show, + zfcp_sysfs_adapter_diag_max_age_store); + static struct attribute *zfcp_adapter_attrs[] = { &dev_attr_adapter_failed.attr, &dev_attr_adapter_in_recovery.attr, @@ -337,6 +382,7 @@ static struct attribute *zfcp_adapter_attrs[] = { &dev_attr_adapter_lic_version.attr, &dev_attr_adapter_status.attr, &dev_attr_adapter_hardware_version.attr, + &dev_attr_adapter_diag_max_age.attr, NULL }; @@ -577,7 +623,7 @@ static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev, return -ENOMEM; retval = zfcp_fsf_exchange_port_data_sync(adapter->qdio, qtcb_port); - if (!retval) + if (retval == 0 || retval == -EAGAIN) retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, qtcb_port->cb_util, qtcb_port->a_util); kfree(qtcb_port); @@ -603,7 +649,7 @@ static int zfcp_sysfs_adapter_ex_config(struct device *dev, return -ENOMEM; retval = zfcp_fsf_exchange_config_data_sync(adapter->qdio, qtcb_config); - if (!retval) + if (retval == 0 || retval == -EAGAIN) *stat_inf = qtcb_config->stat_info; kfree(qtcb_config); @@ -664,3 +710,123 @@ struct device_attribute *zfcp_sysfs_shost_attrs[] = { &dev_attr_queue_full, NULL }; + +static ssize_t zfcp_sysfs_adapter_diag_b2b_credit_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); + struct zfcp_diag_header *diag_hdr; + struct fc_els_flogi *nsp; + ssize_t rc = -ENOLINK; + unsigned long flags; + unsigned int status; + + if (!adapter) + return -ENODEV; + + status = atomic_read(&adapter->status); + if (0 == (status & ZFCP_STATUS_COMMON_OPEN) || + 0 == (status & ZFCP_STATUS_COMMON_UNBLOCKED) || + 0 != (status & ZFCP_STATUS_COMMON_ERP_FAILED)) + goto out; + + diag_hdr = &adapter->diagnostics->config_data.header; + + rc = zfcp_diag_update_buffer_limited( + adapter, diag_hdr, zfcp_diag_update_config_data_buffer); + if (rc != 0) + goto out; + + spin_lock_irqsave(&diag_hdr->access_lock, flags); + /* nport_serv_param doesn't contain the ELS_Command code */ + nsp = (struct fc_els_flogi *)((unsigned long) + adapter->diagnostics->config_data + .data.nport_serv_param - + sizeof(u32)); + + rc = scnprintf(buf, 5 + 2, "%hu\n", + be16_to_cpu(nsp->fl_csp.sp_bb_cred)); + spin_unlock_irqrestore(&diag_hdr->access_lock, flags); + +out: + zfcp_ccw_adapter_put(adapter); + return rc; +} +static ZFCP_DEV_ATTR(adapter_diag, b2b_credit, 0400, + zfcp_sysfs_adapter_diag_b2b_credit_show, NULL); + +#define ZFCP_DEFINE_DIAG_SFP_ATTR(_name, _qtcb_member, _prtsize, _prtfmt) \ + static ssize_t zfcp_sysfs_adapter_diag_sfp_##_name##_show( \ + struct device *dev, struct device_attribute *attr, char *buf) \ + { \ + struct zfcp_adapter *const adapter = \ + zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); \ + struct zfcp_diag_header *diag_hdr; \ + ssize_t rc = -ENOLINK; \ + unsigned long flags; \ + unsigned int status; \ + \ + if (!adapter) \ + return -ENODEV; \ + \ + status = atomic_read(&adapter->status); \ + if (0 == (status & ZFCP_STATUS_COMMON_OPEN) || \ + 0 == (status & ZFCP_STATUS_COMMON_UNBLOCKED) || \ + 0 != (status & ZFCP_STATUS_COMMON_ERP_FAILED)) \ + goto out; \ + \ + if (!zfcp_diag_support_sfp(adapter)) { \ + rc = -EOPNOTSUPP; \ + goto out; \ + } \ + \ + diag_hdr = &adapter->diagnostics->port_data.header; \ + \ + rc = zfcp_diag_update_buffer_limited( \ + adapter, diag_hdr, zfcp_diag_update_port_data_buffer); \ + if (rc != 0) \ + goto out; \ + \ + spin_lock_irqsave(&diag_hdr->access_lock, flags); \ + rc = scnprintf( \ + buf, (_prtsize) + 2, _prtfmt "\n", \ + adapter->diagnostics->port_data.data._qtcb_member); \ + spin_unlock_irqrestore(&diag_hdr->access_lock, flags); \ + \ + out: \ + zfcp_ccw_adapter_put(adapter); \ + return rc; \ + } \ + static ZFCP_DEV_ATTR(adapter_diag_sfp, _name, 0400, \ + zfcp_sysfs_adapter_diag_sfp_##_name##_show, NULL) + +ZFCP_DEFINE_DIAG_SFP_ATTR(temperature, temperature, 5, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(vcc, vcc, 5, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(tx_bias, tx_bias, 5, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(tx_power, tx_power, 5, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(rx_power, rx_power, 5, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(port_tx_type, sfp_flags.port_tx_type, 2, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(optical_port, sfp_flags.optical_port, 1, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(sfp_invalid, sfp_flags.sfp_invalid, 1, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(connector_type, sfp_flags.connector_type, 1, "%hu"); +ZFCP_DEFINE_DIAG_SFP_ATTR(fec_active, sfp_flags.fec_active, 1, "%hu"); + +static struct attribute *zfcp_sysfs_diag_attrs[] = { + &dev_attr_adapter_diag_sfp_temperature.attr, + &dev_attr_adapter_diag_sfp_vcc.attr, + &dev_attr_adapter_diag_sfp_tx_bias.attr, + &dev_attr_adapter_diag_sfp_tx_power.attr, + &dev_attr_adapter_diag_sfp_rx_power.attr, + &dev_attr_adapter_diag_sfp_port_tx_type.attr, + &dev_attr_adapter_diag_sfp_optical_port.attr, + &dev_attr_adapter_diag_sfp_sfp_invalid.attr, + &dev_attr_adapter_diag_sfp_connector_type.attr, + &dev_attr_adapter_diag_sfp_fec_active.attr, + &dev_attr_adapter_diag_b2b_credit.attr, + NULL, +}; + +const struct attribute_group zfcp_sysfs_diag_attr_group = { + .name = "diagnostics", + .attrs = zfcp_sysfs_diag_attrs, +}; |