mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
scsi: zfcp: diagnostics buffer caching and use for exchange port data
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>
This commit is contained in:
parent
92953c6e0a
commit
7e418833e6
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_dbf.o zfcp_erp.o \
|
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_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
|
obj-$(CONFIG_ZFCP) += zfcp.o
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Module interface and handling of zfcp data structures.
|
* Module interface and handling of zfcp data structures.
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2002, 2017
|
* Copyright IBM Corp. 2002, 2018
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -25,6 +25,7 @@
|
|||||||
* Martin Petermann
|
* Martin Petermann
|
||||||
* Sven Schuetz
|
* Sven Schuetz
|
||||||
* Steffen Maier
|
* Steffen Maier
|
||||||
|
* Benjamin Block
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define KMSG_COMPONENT "zfcp"
|
#define KMSG_COMPONENT "zfcp"
|
||||||
@ -36,6 +37,7 @@
|
|||||||
#include "zfcp_ext.h"
|
#include "zfcp_ext.h"
|
||||||
#include "zfcp_fc.h"
|
#include "zfcp_fc.h"
|
||||||
#include "zfcp_reqlist.h"
|
#include "zfcp_reqlist.h"
|
||||||
|
#include "zfcp_diag.h"
|
||||||
|
|
||||||
#define ZFCP_BUS_ID_SIZE 20
|
#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;
|
adapter->erp_action.adapter = adapter;
|
||||||
|
|
||||||
|
if (zfcp_diag_adapter_setup(adapter))
|
||||||
|
goto failed;
|
||||||
|
|
||||||
if (zfcp_qdio_setup(adapter))
|
if (zfcp_qdio_setup(adapter))
|
||||||
goto failed;
|
goto failed;
|
||||||
|
|
||||||
@ -449,6 +454,7 @@ void zfcp_adapter_release(struct kref *ref)
|
|||||||
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
|
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
|
||||||
zfcp_fc_gs_destroy(adapter);
|
zfcp_fc_gs_destroy(adapter);
|
||||||
zfcp_free_low_mem_buffers(adapter);
|
zfcp_free_low_mem_buffers(adapter);
|
||||||
|
zfcp_diag_adapter_free(adapter);
|
||||||
kfree(adapter->req_list);
|
kfree(adapter->req_list);
|
||||||
kfree(adapter->fc_stats);
|
kfree(adapter->fc_stats);
|
||||||
kfree(adapter->stats_reset_data);
|
kfree(adapter->stats_reset_data);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Global definitions for the zfcp device driver.
|
* Global definitions for the zfcp device driver.
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2002, 2017
|
* Copyright IBM Corp. 2002, 2018
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ZFCP_DEF_H
|
#ifndef ZFCP_DEF_H
|
||||||
@ -198,6 +198,7 @@ struct zfcp_adapter {
|
|||||||
struct device_dma_parameters dma_parms;
|
struct device_dma_parameters dma_parms;
|
||||||
struct zfcp_fc_events events;
|
struct zfcp_fc_events events;
|
||||||
unsigned long next_port_scan;
|
unsigned long next_port_scan;
|
||||||
|
struct zfcp_diag_adapter *diagnostics;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct zfcp_port {
|
struct zfcp_port {
|
||||||
|
93
drivers/s390/scsi/zfcp_diag.c
Normal file
93
drivers/s390/scsi/zfcp_diag.c
Normal file
@ -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);
|
||||||
|
}
|
60
drivers/s390/scsi/zfcp_diag.h
Normal file
60
drivers/s390/scsi/zfcp_diag.h
Normal file
@ -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 */
|
@ -11,6 +11,7 @@
|
|||||||
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
||||||
|
|
||||||
#include <linux/blktrace_api.h>
|
#include <linux/blktrace_api.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <scsi/fc/fc_els.h>
|
#include <scsi/fc/fc_els.h>
|
||||||
@ -19,6 +20,7 @@
|
|||||||
#include "zfcp_dbf.h"
|
#include "zfcp_dbf.h"
|
||||||
#include "zfcp_qdio.h"
|
#include "zfcp_qdio.h"
|
||||||
#include "zfcp_reqlist.h"
|
#include "zfcp_reqlist.h"
|
||||||
|
#include "zfcp_diag.h"
|
||||||
|
|
||||||
/* timeout for FSF requests sent during scsi_eh: abort or FCP TMF */
|
/* timeout for FSF requests sent during scsi_eh: abort or FCP TMF */
|
||||||
#define ZFCP_FSF_SCSI_ER_TIMEOUT (10*HZ)
|
#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)
|
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 *qtcb = req->qtcb;
|
||||||
|
struct fsf_qtcb_bottom_port *bottom = &qtcb->bottom.port;
|
||||||
|
|
||||||
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
|
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (qtcb->header.fsf_status) {
|
switch (qtcb->header.fsf_status) {
|
||||||
case FSF_GOOD:
|
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);
|
zfcp_fsf_exchange_port_evaluate(req);
|
||||||
break;
|
break;
|
||||||
case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
|
case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
|
||||||
|
zfcp_diag_update_xdata(diag_hdr, bottom, true);
|
||||||
req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE;
|
req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE;
|
||||||
|
|
||||||
zfcp_fsf_exchange_port_evaluate(req);
|
zfcp_fsf_exchange_port_evaluate(req);
|
||||||
|
Loading…
Reference in New Issue
Block a user