diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/scsi/zfcp_diag.c | 129 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_diag.h | 11 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_sysfs.c | 5 |
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", \ |