]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
scsi: zfcp: diagnostics buffer caching and use for exchange port data
authorBenjamin Block <bblock@linux.ibm.com>
Fri, 25 Oct 2019 16:12:44 +0000 (18:12 +0200)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 29 Oct 2019 02:16:14 +0000 (22:16 -0400)
The FCP channel exposes two central interfaces to receive information about
the local FCP-Adapter/-Port: Exchange Port and Exchange Config Data. Using
these commands can negatively impact the adapter if we allow them to be
sent at a very high rate.

The later parts of this patchset will introduce new user-interfaces to
receive more diagnostics from the adapter. To prevent any negative impact
from using those, this patch adds a simple caching-mechanism that will
prevent a malicious/faulty userspace-application from generating an
abnormal high amount of Exchange Port/Config Data traffic.

Relevant diagnostic data that is received via Exchange Config/Port Data is
cached in buffers associated with the corresponding adapter-struct.  Each
buffer is associated with a timestamp that signals how old the data is,
and, added via a following patch in this series, lets userspace-interfaces
determine when the data is too old and needs to be updated.

Buffer-updates are made during the normal response path of the
corresponding command. With this patch only the output of the Exchange Port
Data command is captured.

Link: https://lore.kernel.org/r/054ca020ce0a53dc0d9176428bea373898944e6a.1572018130.git.bblock@linux.ibm.com
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/s390/scsi/Makefile
drivers/s390/scsi/zfcp_aux.c
drivers/s390/scsi/zfcp_def.h
drivers/s390/scsi/zfcp_diag.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_diag.h [new file with mode: 0644]
drivers/s390/scsi/zfcp_fsf.c

index 9dda431ec8f3ff036a94e0bad7311721cf86e922..352056eb0dd106bd1d730bc4a84e38fddcac4602 100644 (file)
@@ -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
index e390f8c6d5f390f3bc7ba8fa61b061f5dac701fb..a19189d7b3f326e1fcbb01fafea6f305f3a801d0 100644 (file)
@@ -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;
 
@@ -449,6 +454,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);
index f5acdb67442ed66b84c72de23db9dcde533480e3..8cc0eefe4ccce52ca448e2096f9ba285b0127650 100644 (file)
@@ -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
@@ -198,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 (file)
index 0000000..fa1b25f
--- /dev/null
@@ -0,0 +1,93 @@
+// 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/errno.h>
+#include <linux/slab.h>
+
+#include "zfcp_diag.h"
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+/* Max age of data in a diagnostics buffer before it needs a refresh (in ms). */
+#define ZFCP_DIAG_MAX_AGE (5 * 1000)
+
+/**
+ * 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;
+
+       /* 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(ZFCP_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_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);
+}
diff --git a/drivers/s390/scsi/zfcp_diag.h b/drivers/s390/scsi/zfcp_diag.h
new file mode 100644 (file)
index 0000000..2a93332
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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.
+ * @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.
+ */
+struct zfcp_diag_adapter {
+       struct {
+               struct zfcp_diag_header         header;
+               struct fsf_qtcb_bottom_port     data;
+       } port_data;
+};
+
+int zfcp_diag_adapter_setup(struct zfcp_adapter *const adapter);
+void zfcp_diag_adapter_free(struct zfcp_adapter *const adapter);
+
+void zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr,
+                           const void *const data, const bool incomplete);
+
+#endif /* ZFCP_DIAG_H */
index d8f0e446fe13ebe3640a315056bc6e6d8f8d4409..883bbfd67a6245792137b4d0478739a0b2f1d9b2 100644 (file)
@@ -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)
@@ -655,16 +657,26 @@ 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);