diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_attr.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_attr.c | 276 |
1 files changed, 271 insertions, 5 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index b17ee62dd1a9..92b3e13e9061 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c | |||
@@ -7,7 +7,6 @@ | |||
7 | #include "qla_def.h" | 7 | #include "qla_def.h" |
8 | 8 | ||
9 | #include <linux/vmalloc.h> | 9 | #include <linux/vmalloc.h> |
10 | #include <scsi/scsi_transport_fc.h> | ||
11 | 10 | ||
12 | /* SYSFS attributes --------------------------------------------------------- */ | 11 | /* SYSFS attributes --------------------------------------------------------- */ |
13 | 12 | ||
@@ -114,7 +113,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off, | |||
114 | struct device, kobj))); | 113 | struct device, kobj))); |
115 | unsigned long flags; | 114 | unsigned long flags; |
116 | 115 | ||
117 | if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size) | 116 | if (!capable(CAP_SYS_ADMIN) || off != 0) |
118 | return 0; | 117 | return 0; |
119 | 118 | ||
120 | /* Read NVRAM. */ | 119 | /* Read NVRAM. */ |
@@ -123,7 +122,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off, | |||
123 | ha->nvram_size); | 122 | ha->nvram_size); |
124 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 123 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
125 | 124 | ||
126 | return (count); | 125 | return ha->nvram_size; |
127 | } | 126 | } |
128 | 127 | ||
129 | static ssize_t | 128 | static ssize_t |
@@ -175,19 +174,150 @@ static struct bin_attribute sysfs_nvram_attr = { | |||
175 | .mode = S_IRUSR | S_IWUSR, | 174 | .mode = S_IRUSR | S_IWUSR, |
176 | .owner = THIS_MODULE, | 175 | .owner = THIS_MODULE, |
177 | }, | 176 | }, |
178 | .size = 0, | 177 | .size = 512, |
179 | .read = qla2x00_sysfs_read_nvram, | 178 | .read = qla2x00_sysfs_read_nvram, |
180 | .write = qla2x00_sysfs_write_nvram, | 179 | .write = qla2x00_sysfs_write_nvram, |
181 | }; | 180 | }; |
182 | 181 | ||
182 | static ssize_t | ||
183 | qla2x00_sysfs_read_optrom(struct kobject *kobj, char *buf, loff_t off, | ||
184 | size_t count) | ||
185 | { | ||
186 | struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, | ||
187 | struct device, kobj))); | ||
188 | |||
189 | if (ha->optrom_state != QLA_SREADING) | ||
190 | return 0; | ||
191 | if (off > ha->optrom_size) | ||
192 | return 0; | ||
193 | if (off + count > ha->optrom_size) | ||
194 | count = ha->optrom_size - off; | ||
195 | |||
196 | memcpy(buf, &ha->optrom_buffer[off], count); | ||
197 | |||
198 | return count; | ||
199 | } | ||
200 | |||
201 | static ssize_t | ||
202 | qla2x00_sysfs_write_optrom(struct kobject *kobj, char *buf, loff_t off, | ||
203 | size_t count) | ||
204 | { | ||
205 | struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, | ||
206 | struct device, kobj))); | ||
207 | |||
208 | if (ha->optrom_state != QLA_SWRITING) | ||
209 | return -EINVAL; | ||
210 | if (off > ha->optrom_size) | ||
211 | return -ERANGE; | ||
212 | if (off + count > ha->optrom_size) | ||
213 | count = ha->optrom_size - off; | ||
214 | |||
215 | memcpy(&ha->optrom_buffer[off], buf, count); | ||
216 | |||
217 | return count; | ||
218 | } | ||
219 | |||
220 | static struct bin_attribute sysfs_optrom_attr = { | ||
221 | .attr = { | ||
222 | .name = "optrom", | ||
223 | .mode = S_IRUSR | S_IWUSR, | ||
224 | .owner = THIS_MODULE, | ||
225 | }, | ||
226 | .size = OPTROM_SIZE_24XX, | ||
227 | .read = qla2x00_sysfs_read_optrom, | ||
228 | .write = qla2x00_sysfs_write_optrom, | ||
229 | }; | ||
230 | |||
231 | static ssize_t | ||
232 | qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, char *buf, loff_t off, | ||
233 | size_t count) | ||
234 | { | ||
235 | struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, | ||
236 | struct device, kobj))); | ||
237 | int val; | ||
238 | |||
239 | if (off) | ||
240 | return 0; | ||
241 | |||
242 | if (sscanf(buf, "%d", &val) != 1) | ||
243 | return -EINVAL; | ||
244 | |||
245 | switch (val) { | ||
246 | case 0: | ||
247 | if (ha->optrom_state != QLA_SREADING && | ||
248 | ha->optrom_state != QLA_SWRITING) | ||
249 | break; | ||
250 | |||
251 | ha->optrom_state = QLA_SWAITING; | ||
252 | vfree(ha->optrom_buffer); | ||
253 | ha->optrom_buffer = NULL; | ||
254 | break; | ||
255 | case 1: | ||
256 | if (ha->optrom_state != QLA_SWAITING) | ||
257 | break; | ||
258 | |||
259 | ha->optrom_state = QLA_SREADING; | ||
260 | ha->optrom_buffer = (uint8_t *)vmalloc(ha->optrom_size); | ||
261 | if (ha->optrom_buffer == NULL) { | ||
262 | qla_printk(KERN_WARNING, ha, | ||
263 | "Unable to allocate memory for optrom retrieval " | ||
264 | "(%x).\n", ha->optrom_size); | ||
265 | |||
266 | ha->optrom_state = QLA_SWAITING; | ||
267 | return count; | ||
268 | } | ||
269 | |||
270 | memset(ha->optrom_buffer, 0, ha->optrom_size); | ||
271 | ha->isp_ops.read_optrom(ha, ha->optrom_buffer, 0, | ||
272 | ha->optrom_size); | ||
273 | break; | ||
274 | case 2: | ||
275 | if (ha->optrom_state != QLA_SWAITING) | ||
276 | break; | ||
277 | |||
278 | ha->optrom_state = QLA_SWRITING; | ||
279 | ha->optrom_buffer = (uint8_t *)vmalloc(ha->optrom_size); | ||
280 | if (ha->optrom_buffer == NULL) { | ||
281 | qla_printk(KERN_WARNING, ha, | ||
282 | "Unable to allocate memory for optrom update " | ||
283 | "(%x).\n", ha->optrom_size); | ||
284 | |||
285 | ha->optrom_state = QLA_SWAITING; | ||
286 | return count; | ||
287 | } | ||
288 | memset(ha->optrom_buffer, 0, ha->optrom_size); | ||
289 | break; | ||
290 | case 3: | ||
291 | if (ha->optrom_state != QLA_SWRITING) | ||
292 | break; | ||
293 | |||
294 | ha->isp_ops.write_optrom(ha, ha->optrom_buffer, 0, | ||
295 | ha->optrom_size); | ||
296 | break; | ||
297 | } | ||
298 | return count; | ||
299 | } | ||
300 | |||
301 | static struct bin_attribute sysfs_optrom_ctl_attr = { | ||
302 | .attr = { | ||
303 | .name = "optrom_ctl", | ||
304 | .mode = S_IWUSR, | ||
305 | .owner = THIS_MODULE, | ||
306 | }, | ||
307 | .size = 0, | ||
308 | .write = qla2x00_sysfs_write_optrom_ctl, | ||
309 | }; | ||
310 | |||
183 | void | 311 | void |
184 | qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha) | 312 | qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha) |
185 | { | 313 | { |
186 | struct Scsi_Host *host = ha->host; | 314 | struct Scsi_Host *host = ha->host; |
187 | 315 | ||
188 | sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr); | 316 | sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr); |
189 | sysfs_nvram_attr.size = ha->nvram_size; | ||
190 | sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr); | 317 | sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr); |
318 | sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_optrom_attr); | ||
319 | sysfs_create_bin_file(&host->shost_gendev.kobj, | ||
320 | &sysfs_optrom_ctl_attr); | ||
191 | } | 321 | } |
192 | 322 | ||
193 | void | 323 | void |
@@ -197,6 +327,12 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) | |||
197 | 327 | ||
198 | sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr); | 328 | sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr); |
199 | sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr); | 329 | sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr); |
330 | sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_optrom_attr); | ||
331 | sysfs_remove_bin_file(&host->shost_gendev.kobj, | ||
332 | &sysfs_optrom_ctl_attr); | ||
333 | |||
334 | if (ha->beacon_blink_led == 1) | ||
335 | ha->isp_ops.beacon_off(ha); | ||
200 | } | 336 | } |
201 | 337 | ||
202 | /* Scsi_Host attributes. */ | 338 | /* Scsi_Host attributes. */ |
@@ -384,6 +520,50 @@ qla2x00_zio_timer_store(struct class_device *cdev, const char *buf, | |||
384 | return strlen(buf); | 520 | return strlen(buf); |
385 | } | 521 | } |
386 | 522 | ||
523 | static ssize_t | ||
524 | qla2x00_beacon_show(struct class_device *cdev, char *buf) | ||
525 | { | ||
526 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
527 | int len = 0; | ||
528 | |||
529 | if (ha->beacon_blink_led) | ||
530 | len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); | ||
531 | else | ||
532 | len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); | ||
533 | return len; | ||
534 | } | ||
535 | |||
536 | static ssize_t | ||
537 | qla2x00_beacon_store(struct class_device *cdev, const char *buf, | ||
538 | size_t count) | ||
539 | { | ||
540 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
541 | int val = 0; | ||
542 | int rval; | ||
543 | |||
544 | if (IS_QLA2100(ha) || IS_QLA2200(ha)) | ||
545 | return -EPERM; | ||
546 | |||
547 | if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) { | ||
548 | qla_printk(KERN_WARNING, ha, | ||
549 | "Abort ISP active -- ignoring beacon request.\n"); | ||
550 | return -EBUSY; | ||
551 | } | ||
552 | |||
553 | if (sscanf(buf, "%d", &val) != 1) | ||
554 | return -EINVAL; | ||
555 | |||
556 | if (val) | ||
557 | rval = ha->isp_ops.beacon_on(ha); | ||
558 | else | ||
559 | rval = ha->isp_ops.beacon_off(ha); | ||
560 | |||
561 | if (rval != QLA_SUCCESS) | ||
562 | count = 0; | ||
563 | |||
564 | return count; | ||
565 | } | ||
566 | |||
387 | static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, | 567 | static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, |
388 | NULL); | 568 | NULL); |
389 | static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); | 569 | static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); |
@@ -398,6 +578,8 @@ static CLASS_DEVICE_ATTR(zio, S_IRUGO | S_IWUSR, qla2x00_zio_show, | |||
398 | qla2x00_zio_store); | 578 | qla2x00_zio_store); |
399 | static CLASS_DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show, | 579 | static CLASS_DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show, |
400 | qla2x00_zio_timer_store); | 580 | qla2x00_zio_timer_store); |
581 | static CLASS_DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show, | ||
582 | qla2x00_beacon_store); | ||
401 | 583 | ||
402 | struct class_device_attribute *qla2x00_host_attrs[] = { | 584 | struct class_device_attribute *qla2x00_host_attrs[] = { |
403 | &class_device_attr_driver_version, | 585 | &class_device_attr_driver_version, |
@@ -411,6 +593,7 @@ struct class_device_attribute *qla2x00_host_attrs[] = { | |||
411 | &class_device_attr_state, | 593 | &class_device_attr_state, |
412 | &class_device_attr_zio, | 594 | &class_device_attr_zio, |
413 | &class_device_attr_zio_timer, | 595 | &class_device_attr_zio_timer, |
596 | &class_device_attr_beacon, | ||
414 | NULL, | 597 | NULL, |
415 | }; | 598 | }; |
416 | 599 | ||
@@ -426,6 +609,49 @@ qla2x00_get_host_port_id(struct Scsi_Host *shost) | |||
426 | } | 609 | } |
427 | 610 | ||
428 | static void | 611 | static void |
612 | qla2x00_get_host_speed(struct Scsi_Host *shost) | ||
613 | { | ||
614 | scsi_qla_host_t *ha = to_qla_host(shost); | ||
615 | uint32_t speed = 0; | ||
616 | |||
617 | switch (ha->link_data_rate) { | ||
618 | case LDR_1GB: | ||
619 | speed = 1; | ||
620 | break; | ||
621 | case LDR_2GB: | ||
622 | speed = 2; | ||
623 | break; | ||
624 | case LDR_4GB: | ||
625 | speed = 4; | ||
626 | break; | ||
627 | } | ||
628 | fc_host_speed(shost) = speed; | ||
629 | } | ||
630 | |||
631 | static void | ||
632 | qla2x00_get_host_port_type(struct Scsi_Host *shost) | ||
633 | { | ||
634 | scsi_qla_host_t *ha = to_qla_host(shost); | ||
635 | uint32_t port_type = FC_PORTTYPE_UNKNOWN; | ||
636 | |||
637 | switch (ha->current_topology) { | ||
638 | case ISP_CFG_NL: | ||
639 | port_type = FC_PORTTYPE_LPORT; | ||
640 | break; | ||
641 | case ISP_CFG_FL: | ||
642 | port_type = FC_PORTTYPE_NLPORT; | ||
643 | break; | ||
644 | case ISP_CFG_N: | ||
645 | port_type = FC_PORTTYPE_PTP; | ||
646 | break; | ||
647 | case ISP_CFG_F: | ||
648 | port_type = FC_PORTTYPE_NPORT; | ||
649 | break; | ||
650 | } | ||
651 | fc_host_port_type(shost) = port_type; | ||
652 | } | ||
653 | |||
654 | static void | ||
429 | qla2x00_get_starget_node_name(struct scsi_target *starget) | 655 | qla2x00_get_starget_node_name(struct scsi_target *starget) |
430 | { | 656 | { |
431 | struct Scsi_Host *host = dev_to_shost(starget->dev.parent); | 657 | struct Scsi_Host *host = dev_to_shost(starget->dev.parent); |
@@ -512,6 +738,41 @@ qla2x00_issue_lip(struct Scsi_Host *shost) | |||
512 | return 0; | 738 | return 0; |
513 | } | 739 | } |
514 | 740 | ||
741 | static struct fc_host_statistics * | ||
742 | qla2x00_get_fc_host_stats(struct Scsi_Host *shost) | ||
743 | { | ||
744 | scsi_qla_host_t *ha = to_qla_host(shost); | ||
745 | int rval; | ||
746 | uint16_t mb_stat[1]; | ||
747 | link_stat_t stat_buf; | ||
748 | struct fc_host_statistics *pfc_host_stat; | ||
749 | |||
750 | pfc_host_stat = &ha->fc_host_stat; | ||
751 | memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics)); | ||
752 | |||
753 | if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) { | ||
754 | rval = qla24xx_get_isp_stats(ha, (uint32_t *)&stat_buf, | ||
755 | sizeof(stat_buf) / 4, mb_stat); | ||
756 | } else { | ||
757 | rval = qla2x00_get_link_status(ha, ha->loop_id, &stat_buf, | ||
758 | mb_stat); | ||
759 | } | ||
760 | if (rval != 0) { | ||
761 | qla_printk(KERN_WARNING, ha, | ||
762 | "Unable to retrieve host statistics (%d).\n", mb_stat[0]); | ||
763 | return pfc_host_stat; | ||
764 | } | ||
765 | |||
766 | pfc_host_stat->link_failure_count = stat_buf.link_fail_cnt; | ||
767 | pfc_host_stat->loss_of_sync_count = stat_buf.loss_sync_cnt; | ||
768 | pfc_host_stat->loss_of_signal_count = stat_buf.loss_sig_cnt; | ||
769 | pfc_host_stat->prim_seq_protocol_err_count = stat_buf.prim_seq_err_cnt; | ||
770 | pfc_host_stat->invalid_tx_word_count = stat_buf.inval_xmit_word_cnt; | ||
771 | pfc_host_stat->invalid_crc_count = stat_buf.inval_crc_cnt; | ||
772 | |||
773 | return pfc_host_stat; | ||
774 | } | ||
775 | |||
515 | struct fc_function_template qla2xxx_transport_functions = { | 776 | struct fc_function_template qla2xxx_transport_functions = { |
516 | 777 | ||
517 | .show_host_node_name = 1, | 778 | .show_host_node_name = 1, |
@@ -520,6 +781,10 @@ struct fc_function_template qla2xxx_transport_functions = { | |||
520 | 781 | ||
521 | .get_host_port_id = qla2x00_get_host_port_id, | 782 | .get_host_port_id = qla2x00_get_host_port_id, |
522 | .show_host_port_id = 1, | 783 | .show_host_port_id = 1, |
784 | .get_host_speed = qla2x00_get_host_speed, | ||
785 | .show_host_speed = 1, | ||
786 | .get_host_port_type = qla2x00_get_host_port_type, | ||
787 | .show_host_port_type = 1, | ||
523 | 788 | ||
524 | .dd_fcrport_size = sizeof(struct fc_port *), | 789 | .dd_fcrport_size = sizeof(struct fc_port *), |
525 | .show_rport_supported_classes = 1, | 790 | .show_rport_supported_classes = 1, |
@@ -536,6 +801,7 @@ struct fc_function_template qla2xxx_transport_functions = { | |||
536 | .show_rport_dev_loss_tmo = 1, | 801 | .show_rport_dev_loss_tmo = 1, |
537 | 802 | ||
538 | .issue_fc_host_lip = qla2x00_issue_lip, | 803 | .issue_fc_host_lip = qla2x00_issue_lip, |
804 | .get_fc_host_stats = qla2x00_get_fc_host_stats, | ||
539 | }; | 805 | }; |
540 | 806 | ||
541 | void | 807 | void |