diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/kvm/Makefile | 9 | ||||
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 338 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.c | 39 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.h | 18 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_scsi.c | 114 |
6 files changed, 499 insertions, 21 deletions
diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile index 5a888704a8d0..4f4e7cf105d4 100644 --- a/drivers/s390/Makefile +++ b/drivers/s390/Makefile | |||
@@ -5,7 +5,7 @@ | |||
5 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w | 5 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w |
6 | 6 | ||
7 | obj-y += s390mach.o sysinfo.o s390_rdev.o | 7 | obj-y += s390mach.o sysinfo.o s390_rdev.o |
8 | obj-y += cio/ block/ char/ crypto/ net/ scsi/ | 8 | obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/ |
9 | 9 | ||
10 | drivers-y += drivers/s390/built-in.o | 10 | drivers-y += drivers/s390/built-in.o |
11 | 11 | ||
diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile new file mode 100644 index 000000000000..4a5ec39f9ca6 --- /dev/null +++ b/drivers/s390/kvm/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # Makefile for kvm guest drivers on s390 | ||
2 | # | ||
3 | # Copyright IBM Corp. 2008 | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify | ||
6 | # it under the terms of the GNU General Public License (version 2 only) | ||
7 | # as published by the Free Software Foundation. | ||
8 | |||
9 | obj-$(CONFIG_VIRTIO) += kvm_virtio.o | ||
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c new file mode 100644 index 000000000000..bbef3764fbf8 --- /dev/null +++ b/drivers/s390/kvm/kvm_virtio.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * kvm_virtio.c - virtio for kvm on s390 | ||
3 | * | ||
4 | * Copyright IBM Corp. 2008 | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License (version 2 only) | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/bootmem.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/virtio.h> | ||
17 | #include <linux/virtio_config.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/virtio_ring.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/kvm_para.h> | ||
22 | #include <asm/kvm_virtio.h> | ||
23 | #include <asm/setup.h> | ||
24 | #include <asm/s390_ext.h> | ||
25 | |||
26 | #define VIRTIO_SUBCODE_64 0x0D00 | ||
27 | |||
28 | /* | ||
29 | * The pointer to our (page) of device descriptions. | ||
30 | */ | ||
31 | static void *kvm_devices; | ||
32 | |||
33 | /* | ||
34 | * Unique numbering for kvm devices. | ||
35 | */ | ||
36 | static unsigned int dev_index; | ||
37 | |||
38 | struct kvm_device { | ||
39 | struct virtio_device vdev; | ||
40 | struct kvm_device_desc *desc; | ||
41 | }; | ||
42 | |||
43 | #define to_kvmdev(vd) container_of(vd, struct kvm_device, vdev) | ||
44 | |||
45 | /* | ||
46 | * memory layout: | ||
47 | * - kvm_device_descriptor | ||
48 | * struct kvm_device_desc | ||
49 | * - configuration | ||
50 | * struct kvm_vqconfig | ||
51 | * - feature bits | ||
52 | * - config space | ||
53 | */ | ||
54 | static struct kvm_vqconfig *kvm_vq_config(const struct kvm_device_desc *desc) | ||
55 | { | ||
56 | return (struct kvm_vqconfig *)(desc + 1); | ||
57 | } | ||
58 | |||
59 | static u8 *kvm_vq_features(const struct kvm_device_desc *desc) | ||
60 | { | ||
61 | return (u8 *)(kvm_vq_config(desc) + desc->num_vq); | ||
62 | } | ||
63 | |||
64 | static u8 *kvm_vq_configspace(const struct kvm_device_desc *desc) | ||
65 | { | ||
66 | return kvm_vq_features(desc) + desc->feature_len * 2; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * The total size of the config page used by this device (incl. desc) | ||
71 | */ | ||
72 | static unsigned desc_size(const struct kvm_device_desc *desc) | ||
73 | { | ||
74 | return sizeof(*desc) | ||
75 | + desc->num_vq * sizeof(struct kvm_vqconfig) | ||
76 | + desc->feature_len * 2 | ||
77 | + desc->config_len; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * This tests (and acknowleges) a feature bit. | ||
82 | */ | ||
83 | static bool kvm_feature(struct virtio_device *vdev, unsigned fbit) | ||
84 | { | ||
85 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
86 | u8 *features; | ||
87 | |||
88 | if (fbit / 8 > desc->feature_len) | ||
89 | return false; | ||
90 | |||
91 | features = kvm_vq_features(desc); | ||
92 | if (!(features[fbit / 8] & (1 << (fbit % 8)))) | ||
93 | return false; | ||
94 | |||
95 | /* | ||
96 | * We set the matching bit in the other half of the bitmap to tell the | ||
97 | * Host we want to use this feature. | ||
98 | */ | ||
99 | features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8)); | ||
100 | return true; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Reading and writing elements in config space | ||
105 | */ | ||
106 | static void kvm_get(struct virtio_device *vdev, unsigned int offset, | ||
107 | void *buf, unsigned len) | ||
108 | { | ||
109 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
110 | |||
111 | BUG_ON(offset + len > desc->config_len); | ||
112 | memcpy(buf, kvm_vq_configspace(desc) + offset, len); | ||
113 | } | ||
114 | |||
115 | static void kvm_set(struct virtio_device *vdev, unsigned int offset, | ||
116 | const void *buf, unsigned len) | ||
117 | { | ||
118 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
119 | |||
120 | BUG_ON(offset + len > desc->config_len); | ||
121 | memcpy(kvm_vq_configspace(desc) + offset, buf, len); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * The operations to get and set the status word just access | ||
126 | * the status field of the device descriptor. set_status will also | ||
127 | * make a hypercall to the host, to tell about status changes | ||
128 | */ | ||
129 | static u8 kvm_get_status(struct virtio_device *vdev) | ||
130 | { | ||
131 | return to_kvmdev(vdev)->desc->status; | ||
132 | } | ||
133 | |||
134 | static void kvm_set_status(struct virtio_device *vdev, u8 status) | ||
135 | { | ||
136 | BUG_ON(!status); | ||
137 | to_kvmdev(vdev)->desc->status = status; | ||
138 | kvm_hypercall1(KVM_S390_VIRTIO_SET_STATUS, | ||
139 | (unsigned long) to_kvmdev(vdev)->desc); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * To reset the device, we use the KVM_VIRTIO_RESET hypercall, using the | ||
144 | * descriptor address. The Host will zero the status and all the | ||
145 | * features. | ||
146 | */ | ||
147 | static void kvm_reset(struct virtio_device *vdev) | ||
148 | { | ||
149 | kvm_hypercall1(KVM_S390_VIRTIO_RESET, | ||
150 | (unsigned long) to_kvmdev(vdev)->desc); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * When the virtio_ring code wants to notify the Host, it calls us here and we | ||
155 | * make a hypercall. We hand the address of the virtqueue so the Host | ||
156 | * knows which virtqueue we're talking about. | ||
157 | */ | ||
158 | static void kvm_notify(struct virtqueue *vq) | ||
159 | { | ||
160 | struct kvm_vqconfig *config = vq->priv; | ||
161 | |||
162 | kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * This routine finds the first virtqueue described in the configuration of | ||
167 | * this device and sets it up. | ||
168 | */ | ||
169 | static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, | ||
170 | unsigned index, | ||
171 | void (*callback)(struct virtqueue *vq)) | ||
172 | { | ||
173 | struct kvm_device *kdev = to_kvmdev(vdev); | ||
174 | struct kvm_vqconfig *config; | ||
175 | struct virtqueue *vq; | ||
176 | int err; | ||
177 | |||
178 | if (index >= kdev->desc->num_vq) | ||
179 | return ERR_PTR(-ENOENT); | ||
180 | |||
181 | config = kvm_vq_config(kdev->desc)+index; | ||
182 | |||
183 | if (add_shared_memory(config->address, | ||
184 | vring_size(config->num, PAGE_SIZE))) { | ||
185 | err = -ENOMEM; | ||
186 | goto out; | ||
187 | } | ||
188 | |||
189 | vq = vring_new_virtqueue(config->num, vdev, (void *) config->address, | ||
190 | kvm_notify, callback); | ||
191 | if (!vq) { | ||
192 | err = -ENOMEM; | ||
193 | goto unmap; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * register a callback token | ||
198 | * The host will sent this via the external interrupt parameter | ||
199 | */ | ||
200 | config->token = (u64) vq; | ||
201 | |||
202 | vq->priv = config; | ||
203 | return vq; | ||
204 | unmap: | ||
205 | remove_shared_memory(config->address, vring_size(config->num, | ||
206 | PAGE_SIZE)); | ||
207 | out: | ||
208 | return ERR_PTR(err); | ||
209 | } | ||
210 | |||
211 | static void kvm_del_vq(struct virtqueue *vq) | ||
212 | { | ||
213 | struct kvm_vqconfig *config = vq->priv; | ||
214 | |||
215 | vring_del_virtqueue(vq); | ||
216 | remove_shared_memory(config->address, | ||
217 | vring_size(config->num, PAGE_SIZE)); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * The config ops structure as defined by virtio config | ||
222 | */ | ||
223 | static struct virtio_config_ops kvm_vq_configspace_ops = { | ||
224 | .feature = kvm_feature, | ||
225 | .get = kvm_get, | ||
226 | .set = kvm_set, | ||
227 | .get_status = kvm_get_status, | ||
228 | .set_status = kvm_set_status, | ||
229 | .reset = kvm_reset, | ||
230 | .find_vq = kvm_find_vq, | ||
231 | .del_vq = kvm_del_vq, | ||
232 | }; | ||
233 | |||
234 | /* | ||
235 | * The root device for the kvm virtio devices. | ||
236 | * This makes them appear as /sys/devices/kvm_s390/0,1,2 not /sys/devices/0,1,2. | ||
237 | */ | ||
238 | static struct device kvm_root = { | ||
239 | .parent = NULL, | ||
240 | .bus_id = "kvm_s390", | ||
241 | }; | ||
242 | |||
243 | /* | ||
244 | * adds a new device and register it with virtio | ||
245 | * appropriate drivers are loaded by the device model | ||
246 | */ | ||
247 | static void add_kvm_device(struct kvm_device_desc *d) | ||
248 | { | ||
249 | struct kvm_device *kdev; | ||
250 | |||
251 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); | ||
252 | if (!kdev) { | ||
253 | printk(KERN_EMERG "Cannot allocate kvm dev %u\n", | ||
254 | dev_index++); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | kdev->vdev.dev.parent = &kvm_root; | ||
259 | kdev->vdev.index = dev_index++; | ||
260 | kdev->vdev.id.device = d->type; | ||
261 | kdev->vdev.config = &kvm_vq_configspace_ops; | ||
262 | kdev->desc = d; | ||
263 | |||
264 | if (register_virtio_device(&kdev->vdev) != 0) { | ||
265 | printk(KERN_ERR "Failed to register kvm device %u\n", | ||
266 | kdev->vdev.index); | ||
267 | kfree(kdev); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * scan_devices() simply iterates through the device page. | ||
273 | * The type 0 is reserved to mean "end of devices". | ||
274 | */ | ||
275 | static void scan_devices(void) | ||
276 | { | ||
277 | unsigned int i; | ||
278 | struct kvm_device_desc *d; | ||
279 | |||
280 | for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { | ||
281 | d = kvm_devices + i; | ||
282 | |||
283 | if (d->type == 0) | ||
284 | break; | ||
285 | |||
286 | add_kvm_device(d); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * we emulate the request_irq behaviour on top of s390 extints | ||
292 | */ | ||
293 | static void kvm_extint_handler(u16 code) | ||
294 | { | ||
295 | void *data = (void *) *(long *) __LC_PFAULT_INTPARM; | ||
296 | u16 subcode = S390_lowcore.cpu_addr; | ||
297 | |||
298 | if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) | ||
299 | return; | ||
300 | |||
301 | vring_interrupt(0, data); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Init function for virtio | ||
306 | * devices are in a single page above top of "normal" mem | ||
307 | */ | ||
308 | static int __init kvm_devices_init(void) | ||
309 | { | ||
310 | int rc; | ||
311 | |||
312 | if (!MACHINE_IS_KVM) | ||
313 | return -ENODEV; | ||
314 | |||
315 | rc = device_register(&kvm_root); | ||
316 | if (rc) { | ||
317 | printk(KERN_ERR "Could not register kvm_s390 root device"); | ||
318 | return rc; | ||
319 | } | ||
320 | |||
321 | if (add_shared_memory((max_pfn) << PAGE_SHIFT, PAGE_SIZE)) { | ||
322 | device_unregister(&kvm_root); | ||
323 | return -ENOMEM; | ||
324 | } | ||
325 | |||
326 | kvm_devices = (void *) (max_pfn << PAGE_SHIFT); | ||
327 | |||
328 | ctl_set_bit(0, 9); | ||
329 | register_external_interrupt(0x2603, kvm_extint_handler); | ||
330 | |||
331 | scan_devices(); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * We do this after core stuff, but before the drivers. | ||
337 | */ | ||
338 | postcore_initcall(kvm_devices_init); | ||
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 7c3f02816e95..9af2330f07a2 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c | |||
@@ -1927,7 +1927,8 @@ zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, | |||
1927 | 1927 | ||
1928 | /* setup new FSF request */ | 1928 | /* setup new FSF request */ |
1929 | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, | 1929 | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, |
1930 | 0, NULL, &lock_flags, &fsf_req); | 1930 | ZFCP_WAIT_FOR_SBAL, NULL, &lock_flags, |
1931 | &fsf_req); | ||
1931 | if (retval) { | 1932 | if (retval) { |
1932 | ZFCP_LOG_INFO("error: Could not create exchange configuration " | 1933 | ZFCP_LOG_INFO("error: Could not create exchange configuration " |
1933 | "data request for adapter %s.\n", | 1934 | "data request for adapter %s.\n", |
@@ -2035,21 +2036,21 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) | |||
2035 | min(FC_SERIAL_NUMBER_SIZE, 17)); | 2036 | min(FC_SERIAL_NUMBER_SIZE, 17)); |
2036 | } | 2037 | } |
2037 | 2038 | ||
2038 | ZFCP_LOG_NORMAL("The adapter %s reported the following " | 2039 | if (fsf_req->erp_action) |
2039 | "characteristics:\n" | 2040 | ZFCP_LOG_NORMAL("The adapter %s reported the following " |
2040 | "WWNN 0x%016Lx, " | 2041 | "characteristics:\n" |
2041 | "WWPN 0x%016Lx, " | 2042 | "WWNN 0x%016Lx, WWPN 0x%016Lx, " |
2042 | "S_ID 0x%06x,\n" | 2043 | "S_ID 0x%06x,\n" |
2043 | "adapter version 0x%x, " | 2044 | "adapter version 0x%x, " |
2044 | "LIC version 0x%x, " | 2045 | "LIC version 0x%x, " |
2045 | "FC link speed %d Gb/s\n", | 2046 | "FC link speed %d Gb/s\n", |
2046 | zfcp_get_busid_by_adapter(adapter), | 2047 | zfcp_get_busid_by_adapter(adapter), |
2047 | (wwn_t) fc_host_node_name(shost), | 2048 | (wwn_t) fc_host_node_name(shost), |
2048 | (wwn_t) fc_host_port_name(shost), | 2049 | (wwn_t) fc_host_port_name(shost), |
2049 | fc_host_port_id(shost), | 2050 | fc_host_port_id(shost), |
2050 | adapter->hydra_version, | 2051 | adapter->hydra_version, |
2051 | adapter->fsf_lic_version, | 2052 | adapter->fsf_lic_version, |
2052 | fc_host_speed(shost)); | 2053 | fc_host_speed(shost)); |
2053 | if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) { | 2054 | if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) { |
2054 | ZFCP_LOG_NORMAL("error: the adapter %s " | 2055 | ZFCP_LOG_NORMAL("error: the adapter %s " |
2055 | "only supports newer control block " | 2056 | "only supports newer control block " |
@@ -2114,8 +2115,10 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) | |||
2114 | zfcp_erp_adapter_shutdown(adapter, 0, 127, fsf_req); | 2115 | zfcp_erp_adapter_shutdown(adapter, 0, 127, fsf_req); |
2115 | return -EIO; | 2116 | return -EIO; |
2116 | case FC_PORTTYPE_NPORT: | 2117 | case FC_PORTTYPE_NPORT: |
2117 | ZFCP_LOG_NORMAL("Switched fabric fibrechannel " | 2118 | if (fsf_req->erp_action) |
2118 | "network detected at adapter %s.\n", | 2119 | ZFCP_LOG_NORMAL("Switched fabric fibrechannel " |
2120 | "network detected at adapter " | ||
2121 | "%s.\n", | ||
2119 | zfcp_get_busid_by_adapter(adapter)); | 2122 | zfcp_get_busid_by_adapter(adapter)); |
2120 | break; | 2123 | break; |
2121 | default: | 2124 | default: |
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 8cce5cc11d50..099970b27001 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h | |||
@@ -213,6 +213,7 @@ | |||
213 | #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 | 213 | #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 |
214 | #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 | 214 | #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 |
215 | #define FSF_FEATURE_UPDATE_ALERT 0x00000100 | 215 | #define FSF_FEATURE_UPDATE_ALERT 0x00000100 |
216 | #define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 | ||
216 | 217 | ||
217 | /* host connection features */ | 218 | /* host connection features */ |
218 | #define FSF_FEATURE_NPIV_MODE 0x00000001 | 219 | #define FSF_FEATURE_NPIV_MODE 0x00000001 |
@@ -340,6 +341,15 @@ struct fsf_qtcb_prefix { | |||
340 | u8 res1[20]; | 341 | u8 res1[20]; |
341 | } __attribute__ ((packed)); | 342 | } __attribute__ ((packed)); |
342 | 343 | ||
344 | struct fsf_statistics_info { | ||
345 | u64 input_req; | ||
346 | u64 output_req; | ||
347 | u64 control_req; | ||
348 | u64 input_mb; | ||
349 | u64 output_mb; | ||
350 | u64 seconds_act; | ||
351 | } __attribute__ ((packed)); | ||
352 | |||
343 | union fsf_status_qual { | 353 | union fsf_status_qual { |
344 | u8 byte[FSF_STATUS_QUALIFIER_SIZE]; | 354 | u8 byte[FSF_STATUS_QUALIFIER_SIZE]; |
345 | u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; | 355 | u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; |
@@ -436,7 +446,8 @@ struct fsf_qtcb_bottom_config { | |||
436 | u32 hardware_version; | 446 | u32 hardware_version; |
437 | u8 serial_number[32]; | 447 | u8 serial_number[32]; |
438 | struct fsf_nport_serv_param plogi_payload; | 448 | struct fsf_nport_serv_param plogi_payload; |
439 | u8 res4[160]; | 449 | struct fsf_statistics_info stat_info; |
450 | u8 res4[112]; | ||
440 | } __attribute__ ((packed)); | 451 | } __attribute__ ((packed)); |
441 | 452 | ||
442 | struct fsf_qtcb_bottom_port { | 453 | struct fsf_qtcb_bottom_port { |
@@ -469,7 +480,10 @@ struct fsf_qtcb_bottom_port { | |||
469 | u64 control_requests; | 480 | u64 control_requests; |
470 | u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */ | 481 | u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */ |
471 | u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */ | 482 | u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */ |
472 | u8 res2[256]; | 483 | u8 cp_util; |
484 | u8 cb_util; | ||
485 | u8 a_util; | ||
486 | u8 res2[253]; | ||
473 | } __attribute__ ((packed)); | 487 | } __attribute__ ((packed)); |
474 | 488 | ||
475 | union fsf_qtcb_bottom { | 489 | union fsf_qtcb_bottom { |
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index f81850624eed..01687559dc06 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c | |||
@@ -40,6 +40,7 @@ static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, | |||
40 | unsigned int, unsigned int); | 40 | unsigned int, unsigned int); |
41 | 41 | ||
42 | static struct device_attribute *zfcp_sysfs_sdev_attrs[]; | 42 | static struct device_attribute *zfcp_sysfs_sdev_attrs[]; |
43 | static struct device_attribute *zfcp_a_stats_attrs[]; | ||
43 | 44 | ||
44 | struct zfcp_data zfcp_data = { | 45 | struct zfcp_data zfcp_data = { |
45 | .scsi_host_template = { | 46 | .scsi_host_template = { |
@@ -61,6 +62,7 @@ struct zfcp_data zfcp_data = { | |||
61 | .use_clustering = 1, | 62 | .use_clustering = 1, |
62 | .sdev_attrs = zfcp_sysfs_sdev_attrs, | 63 | .sdev_attrs = zfcp_sysfs_sdev_attrs, |
63 | .max_sectors = ZFCP_MAX_SECTORS, | 64 | .max_sectors = ZFCP_MAX_SECTORS, |
65 | .shost_attrs = zfcp_a_stats_attrs, | ||
64 | }, | 66 | }, |
65 | .driver_version = ZFCP_VERSION, | 67 | .driver_version = ZFCP_VERSION, |
66 | }; | 68 | }; |
@@ -809,4 +811,116 @@ static struct device_attribute *zfcp_sysfs_sdev_attrs[] = { | |||
809 | NULL | 811 | NULL |
810 | }; | 812 | }; |
811 | 813 | ||
814 | static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev, | ||
815 | struct device_attribute *attr, | ||
816 | char *buf) | ||
817 | { | ||
818 | struct Scsi_Host *scsi_host = dev_to_shost(dev); | ||
819 | struct fsf_qtcb_bottom_port *qtcb_port; | ||
820 | int retval; | ||
821 | struct zfcp_adapter *adapter; | ||
822 | |||
823 | adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; | ||
824 | if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) | ||
825 | return -EOPNOTSUPP; | ||
826 | |||
827 | qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL); | ||
828 | if (!qtcb_port) | ||
829 | return -ENOMEM; | ||
830 | |||
831 | retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port); | ||
832 | if (!retval) | ||
833 | retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, | ||
834 | qtcb_port->cb_util, qtcb_port->a_util); | ||
835 | kfree(qtcb_port); | ||
836 | return retval; | ||
837 | } | ||
838 | |||
839 | static int zfcp_sysfs_adapter_ex_config(struct device *dev, | ||
840 | struct fsf_statistics_info *stat_inf) | ||
841 | { | ||
842 | int retval; | ||
843 | struct fsf_qtcb_bottom_config *qtcb_config; | ||
844 | struct Scsi_Host *scsi_host = dev_to_shost(dev); | ||
845 | struct zfcp_adapter *adapter; | ||
846 | |||
847 | adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; | ||
848 | if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) | ||
849 | return -EOPNOTSUPP; | ||
850 | |||
851 | qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), | ||
852 | GFP_KERNEL); | ||
853 | if (!qtcb_config) | ||
854 | return -ENOMEM; | ||
855 | |||
856 | retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config); | ||
857 | if (!retval) | ||
858 | *stat_inf = qtcb_config->stat_info; | ||
859 | |||
860 | kfree(qtcb_config); | ||
861 | return retval; | ||
862 | } | ||
863 | |||
864 | static ssize_t zfcp_sysfs_adapter_request_show(struct device *dev, | ||
865 | struct device_attribute *attr, | ||
866 | char *buf) | ||
867 | { | ||
868 | struct fsf_statistics_info stat_info; | ||
869 | int retval; | ||
870 | |||
871 | retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); | ||
872 | if (retval) | ||
873 | return retval; | ||
874 | |||
875 | return sprintf(buf, "%llu %llu %llu\n", | ||
876 | (unsigned long long) stat_info.input_req, | ||
877 | (unsigned long long) stat_info.output_req, | ||
878 | (unsigned long long) stat_info.control_req); | ||
879 | } | ||
880 | |||
881 | static ssize_t zfcp_sysfs_adapter_mb_show(struct device *dev, | ||
882 | struct device_attribute *attr, | ||
883 | char *buf) | ||
884 | { | ||
885 | struct fsf_statistics_info stat_info; | ||
886 | int retval; | ||
887 | |||
888 | retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); | ||
889 | if (retval) | ||
890 | return retval; | ||
891 | |||
892 | return sprintf(buf, "%llu %llu\n", | ||
893 | (unsigned long long) stat_info.input_mb, | ||
894 | (unsigned long long) stat_info.output_mb); | ||
895 | } | ||
896 | |||
897 | static ssize_t zfcp_sysfs_adapter_sec_active_show(struct device *dev, | ||
898 | struct device_attribute *attr, | ||
899 | char *buf) | ||
900 | { | ||
901 | struct fsf_statistics_info stat_info; | ||
902 | int retval; | ||
903 | |||
904 | retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); | ||
905 | if (retval) | ||
906 | return retval; | ||
907 | |||
908 | return sprintf(buf, "%llu\n", | ||
909 | (unsigned long long) stat_info.seconds_act); | ||
910 | } | ||
911 | |||
912 | static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL); | ||
913 | static DEVICE_ATTR(requests, S_IRUGO, zfcp_sysfs_adapter_request_show, NULL); | ||
914 | static DEVICE_ATTR(megabytes, S_IRUGO, zfcp_sysfs_adapter_mb_show, NULL); | ||
915 | static DEVICE_ATTR(seconds_active, S_IRUGO, | ||
916 | zfcp_sysfs_adapter_sec_active_show, NULL); | ||
917 | |||
918 | static struct device_attribute *zfcp_a_stats_attrs[] = { | ||
919 | &dev_attr_utilization, | ||
920 | &dev_attr_requests, | ||
921 | &dev_attr_megabytes, | ||
922 | &dev_attr_seconds_active, | ||
923 | NULL | ||
924 | }; | ||
925 | |||
812 | #undef ZFCP_LOG_AREA | 926 | #undef ZFCP_LOG_AREA |