aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/scsi')
-rw-r--r--drivers/s390/scsi/zfcp_diag.c129
-rw-r--r--drivers/s390/scsi/zfcp_diag.h11
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c5
3 files changed, 145 insertions, 0 deletions
diff --git a/drivers/s390/scsi/zfcp_diag.c b/drivers/s390/scsi/zfcp_diag.c
index 8df3ace6135d..9c5a0f3431ca 100644
--- a/drivers/s390/scsi/zfcp_diag.c
+++ b/drivers/s390/scsi/zfcp_diag.c
@@ -22,6 +22,8 @@
/* Max age of data in a diagnostics buffer before it needs a refresh (in ms). */
#define ZFCP_DIAG_MAX_AGE (5 * 1000)
+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.
@@ -143,3 +145,130 @@ void zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr,
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;
+}
+
+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_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) >= ZFCP_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(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
index 2deebccda17d..7ff8fb0bb735 100644
--- a/drivers/s390/scsi/zfcp_diag.h
+++ b/drivers/s390/scsi/zfcp_diag.h
@@ -71,6 +71,17 @@ 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_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.
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 6c0cea71ae5d..a2fa3db5695d 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -693,6 +693,11 @@ struct device_attribute *zfcp_sysfs_shost_attrs[] = {
\
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", \