|
@@ -8,6 +8,7 @@
|
|
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/export.h>
|
|
#include <linux/export.h>
|
|
|
|
+#include <linux/mutex.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
@@ -20,7 +21,29 @@
|
|
#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001
|
|
#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001
|
|
#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001
|
|
#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001
|
|
|
|
|
|
-#define SCLP_RECONFIG_PCI_ATPYE 2
|
|
|
|
|
|
+#define SCLP_ATYPE_PCI 2
|
|
|
|
+
|
|
|
|
+#define SCLP_ERRNOTIFY_AQ_REPAIR 1
|
|
|
|
+#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2
|
|
|
|
+
|
|
|
|
+static DEFINE_MUTEX(sclp_pci_mutex);
|
|
|
|
+static struct sclp_register sclp_pci_event = {
|
|
|
|
+ .send_mask = EVTYP_ERRNOTIFY_MASK,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct err_notify_evbuf {
|
|
|
|
+ struct evbuf_header header;
|
|
|
|
+ u8 action;
|
|
|
|
+ u8 atype;
|
|
|
|
+ u32 fh;
|
|
|
|
+ u32 fid;
|
|
|
|
+ u8 data[0];
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct err_notify_sccb {
|
|
|
|
+ struct sccb_header header;
|
|
|
|
+ struct err_notify_evbuf evbuf;
|
|
|
|
+} __packed;
|
|
|
|
|
|
struct pci_cfg_sccb {
|
|
struct pci_cfg_sccb {
|
|
struct sccb_header header;
|
|
struct sccb_header header;
|
|
@@ -43,7 +66,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
sccb->header.length = PAGE_SIZE;
|
|
sccb->header.length = PAGE_SIZE;
|
|
- sccb->atype = SCLP_RECONFIG_PCI_ATPYE;
|
|
|
|
|
|
+ sccb->atype = SCLP_ATYPE_PCI;
|
|
sccb->aid = fid;
|
|
sccb->aid = fid;
|
|
rc = sclp_sync_request(cmd, sccb);
|
|
rc = sclp_sync_request(cmd, sccb);
|
|
if (rc)
|
|
if (rc)
|
|
@@ -74,3 +97,96 @@ int sclp_pci_deconfigure(u32 fid)
|
|
return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid);
|
|
return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(sclp_pci_deconfigure);
|
|
EXPORT_SYMBOL(sclp_pci_deconfigure);
|
|
|
|
+
|
|
|
|
+static void sclp_pci_callback(struct sclp_req *req, void *data)
|
|
|
|
+{
|
|
|
|
+ struct completion *completion = data;
|
|
|
|
+
|
|
|
|
+ complete(completion);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int sclp_pci_check_report(struct zpci_report_error_header *report)
|
|
|
|
+{
|
|
|
|
+ if (report->version != 1)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (report->action != SCLP_ERRNOTIFY_AQ_REPAIR &&
|
|
|
|
+ report->action != SCLP_ERRNOTIFY_AQ_INFO_LOG)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid)
|
|
|
|
+{
|
|
|
|
+ DECLARE_COMPLETION_ONSTACK(completion);
|
|
|
|
+ struct err_notify_sccb *sccb;
|
|
|
|
+ struct sclp_req req = {0};
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = sclp_pci_check_report(report);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&sclp_pci_mutex);
|
|
|
|
+ ret = sclp_register(&sclp_pci_event);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+
|
|
|
|
+ if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) {
|
|
|
|
+ ret = -EOPNOTSUPP;
|
|
|
|
+ goto out_unregister;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
|
|
|
+ if (!sccb) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto out_unregister;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ req.callback_data = &completion;
|
|
|
|
+ req.callback = sclp_pci_callback;
|
|
|
|
+ req.command = SCLP_CMDW_WRITE_EVENT_DATA;
|
|
|
|
+ req.status = SCLP_REQ_FILLED;
|
|
|
|
+ req.sccb = sccb;
|
|
|
|
+
|
|
|
|
+ sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length;
|
|
|
|
+ sccb->evbuf.header.type = EVTYP_ERRNOTIFY;
|
|
|
|
+ sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length;
|
|
|
|
+
|
|
|
|
+ sccb->evbuf.action = report->action;
|
|
|
|
+ sccb->evbuf.atype = SCLP_ATYPE_PCI;
|
|
|
|
+ sccb->evbuf.fh = fh;
|
|
|
|
+ sccb->evbuf.fid = fid;
|
|
|
|
+
|
|
|
|
+ memcpy(sccb->evbuf.data, report->data, report->length);
|
|
|
|
+
|
|
|
|
+ ret = sclp_add_request(&req);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out_free_req;
|
|
|
|
+
|
|
|
|
+ wait_for_completion(&completion);
|
|
|
|
+ if (req.status != SCLP_REQ_DONE) {
|
|
|
|
+ pr_warn("request failed (status=0x%02x)\n",
|
|
|
|
+ req.status);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ goto out_free_req;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (sccb->header.response_code != 0x0020) {
|
|
|
|
+ pr_warn("request failed with response code 0x%x\n",
|
|
|
|
+ sccb->header.response_code);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out_free_req:
|
|
|
|
+ free_page((unsigned long) sccb);
|
|
|
|
+out_unregister:
|
|
|
|
+ sclp_unregister(&sclp_pci_event);
|
|
|
|
+out_unlock:
|
|
|
|
+ mutex_unlock(&sclp_pci_mutex);
|
|
|
|
+ return ret;
|
|
|
|
+}
|